Комментарии 76
Аккаунт угнали?
Нет, просто я в очередной раз пишу более-менее нормальную техническую статью, а не обзор очередной версии FF и/или Убунты, как некоторые.
А получаю в ответ от хабравчан кучу минусов в карму, как и за прошлую свою статью, тоже чисто техническую, посвященную 3TB дискам. Вывод очевиден.
А получаю в ответ от хабравчан кучу минусов в карму, как и за прошлую свою статью, тоже чисто техническую, посвященную 3TB дискам. Вывод очевиден.
Не обижайтесь, но статья такая. Я знаком с Си++ и сам пишу статьи по программированию. И ставлю минусы обзором FF/Убунты и тому подобному. И плюсую программистские статьи. Но этой плюс поставить не смог — воздержался.
Я не обижаюсь, меня поражает уровень хабражителей, когда постишь 0-day комикс в ХабраЮмор получаешь гарантированно +50 в карму, а за техноманьячный пост про BOOL/BOOLEAN получаешь одни минуса. А хотелось бы наоборот.
Наверное, именно поэтому Хабраюмор убрали. Печаль.
Наверное, именно поэтому Хабраюмор убрали. Печаль.
FYI: Я прочитал вашу статью и изначально поставил ей минус. Это было обоснованно тем, что вы использовали тип BOOLEAN, про который я впервые слышу в C++ (я не профи C++). И вывод «никогда не пользуйтесь BOOL, только BOOLEAN» удивил меня, так как я приверженец bool. Сейчас же я понимаю, что основной призыва не мешать BOOL и bool, но оценку уже не изменить, хотя статья действительно хорошая.
Это да. Я уже забыл как свежие инвайты выглядят. Написать программистскую статью на 100, это ого-го…
Доброе утро, я и есть тот самый «некоторый», которого Вы имеете в виду: четыре последние захабренные блогозаписи во блоге о Mozilla Firefox (про работу с мобильною связью, про управление вибрацией, про переход на новую строку во всплывающих подсказках, про text-align-last) сочинил и опубликовал именно я.
С удивлением вижу, что, по-видимому, все мои статьи минусуют за то, что они посвящены использованию языков JavaScript да CSS в Firefox, а не Си++ в Visual Studio — и потому, стало быть, не технические да не программистские.
Вы уверены, что хотите жить в таком Хабрахабре, где все поступают так же, как Вы — минусуют статьи, далёкие от круга собственных интересов? Убеждён, что тогда Вашу статью одни читатели минусовали бы за то, что она не про iPhone, другие — за то, что она не про MD5 (да и вообще не про хэширование!…), третьи — за то, что она не про GPS (и даже не про ГЛОНАСС!…), четвёртые — за то, что она не помогает (совершенно, совершенно ничем не помогает, ну никакой пользы не приносит!…) в настройке беспроводных маршрутизаторов, пятые — за то, что она не про ВКонтакте (и даже ни слова в ней нет про LiveJournal или Twitter), а шестые — за то, что она не имеет никакого отношения не только к лечению компьютерных вирусов, но и к таможенным пошлинам на микропроцессоры.
Чтобы Вы почувствовали, как это будет, для начала позвольте поставить Вашей статье минус за то, что в ней, как ни старайся, не найдёшь ни строчки джаваскрипта или CSS.Веб-программистам эта статья совершенно бесполезна — ну и чего ей делать на заглавной странице Хабрахабра? Имею основания полагать, что и программисты на PHP, Python, Ruby и Erlang не найдут в ней ничего нового для себя. А тех лиц, которые сочиняют обзоры Убунты, наверняка не порадуют все вот эти чисто проприетарные виндоусовские заморочки — windows.h, WinDef.h — ну кого это может заинтересовать в мире открытых и свободных операционных систем?
И так далее.
Пишите во блоге о Си++ про свой Си++, а мне, уж пожалуйста, не мешайте писать во блоге Firefox про мой Firefox, не лезьте со своими минусами. Ведь на Хабрахабре не просто так создавались ≈280 различных блогов, посвящённые ≈280 совершенно различным направлениям и обстоятельствам айтишной жизни.
С удивлением вижу, что, по-видимому, все мои статьи минусуют за то, что они посвящены использованию языков JavaScript да CSS в Firefox, а не Си++ в Visual Studio — и потому, стало быть, не технические да не программистские.
Вы уверены, что хотите жить в таком Хабрахабре, где все поступают так же, как Вы — минусуют статьи, далёкие от круга собственных интересов? Убеждён, что тогда Вашу статью одни читатели минусовали бы за то, что она не про iPhone, другие — за то, что она не про MD5 (да и вообще не про хэширование!…), третьи — за то, что она не про GPS (и даже не про ГЛОНАСС!…), четвёртые — за то, что она не помогает (совершенно, совершенно ничем не помогает, ну никакой пользы не приносит!…) в настройке беспроводных маршрутизаторов, пятые — за то, что она не про ВКонтакте (и даже ни слова в ней нет про LiveJournal или Twitter), а шестые — за то, что она не имеет никакого отношения не только к лечению компьютерных вирусов, но и к таможенным пошлинам на микропроцессоры.
Чтобы Вы почувствовали, как это будет, для начала позвольте поставить Вашей статье минус за то, что в ней, как ни старайся, не найдёшь ни строчки джаваскрипта или CSS.
И так далее.
Пишите во блоге о Си++ про свой Си++, а мне, уж пожалуйста, не мешайте писать во блоге Firefox про мой Firefox, не лезьте со своими минусами. Ведь на Хабрахабре не просто так создавались ≈280 различных блогов, посвящённые ≈280 совершенно различным направлениям и обстоятельствам айтишной жизни.
>Пишите во блоге о Си++ про свой Си++, а мне, уж пожалуйста, не мешайте писать во блоге Firefox
Думаю, что проблема не в этом, потому что, чтобы заминусовать статью по «тематическим» соображениям нужно ее сначала увидеть. А увидеть, не подписавшись на тематический блог, можно только тогда, когда статья появляется на главной.
Но, появившись на главной, статья уже имеет достаточное количество плюсов и единичные минусы ей, по большому счету, никак не помешают.
Думаю, что проблема не в этом, потому что, чтобы заминусовать статью по «тематическим» соображениям нужно ее сначала увидеть. А увидеть, не подписавшись на тематический блог, можно только тогда, когда статья появляется на главной.
Но, появившись на главной, статья уже имеет достаточное количество плюсов и единичные минусы ей, по большому счету, никак не помешают.
Ну да, а то я не знаю, как читают статьи не на главной, а в ленте, но всё же не подписываясь вручную на каждый тематический блог.
Сперва читатель Хабрахабра лезет в настройки ленты и жмякает там по первой галочке («Читать все»):
![[скриншот]](https://habrastorage.org/getpro/habr/post_images/0d3/8af/410/0d38af41015fe17dda1f33d1983c1b56.gif)
Затем читатель с ужасом и отвращением видит, что бóльшая часть его ленты заполнена блогозаписямине про Си++. Какой кошмар! С этим надо что-то делать — например, заминусовать все их, насколько заряда хватит…
Сперва читатель Хабрахабра лезет в настройки ленты и жмякает там по первой галочке («Читать все»):
![[скриншот]](https://habrastorage.org/getpro/habr/post_images/0d3/8af/410/0d38af41015fe17dda1f33d1983c1b56.gif)
Затем читатель с ужасом и отвращением видит, что бóльшая часть его ленты заполнена блогозаписями
А будет разоблачение черной магии? Пояснение, в чем же причина? Я вот так сразу не пойму, почему вначале выдает Equals. Правда на С++ не писал уже много-много лет.
sizeof(bool) != sizeof(BOOL) для данного случая
Строка:
кастит функцию возвращающую bool на функцию возвращающую int (BOOL)
Компилятор наверное ругнулся по этому поводу?
Строка:
(CallBack IsEqual = (CallBack)CompareInt;
кастит функцию возвращающую bool на функцию возвращающую int (BOOL)
Компилятор наверное ругнулся по этому поводу?
Нет, не ругнулся. Интересно, а PVS-Studio эту ситуацию как-то определяет? Вопрос знатокам.
Такое определять легко, но смысла в этом мало, так как будет огромное количество false positives, так как C-style cast рассчитаны в том числе и на приведение несовместимых типов данных. Используйте нормальный static_cast в C++ и он вам будет давать ошибки компиляции когда вы это интуитивно ожидаете.
А что тут отлавливать? Явное приведение типа? Как тут уже заметили — ложных срабатываний будет 99.99% :-(. Собственно в реальном коде программиста должно остановить, что CallBack IsEqual = CompareInt; не будет компилироваться. А раз он гвоздиком прибил — значит так и надо.
А sizeof(bool) == sizeof(BOOL)? Там вообще нормальное приведение указателя на функцию?
Это не чёрная магия, а зоопарк определений в виндовых включаемых файлах.
Это Майкрософт подложил свинью в виде своего проприетарного типа BOOL, который на самом деле int. Тянется, видимо, еще со времен Windows 2.0
Прям уж свинью. В нормальных книгах пишут, что BOOL это int. Да и просто программирую я в Windows сложно этого не заметить. Не вижу ничего плохого, что BOOL такой, какой он есть.
Написал про разоблачение черной магии в UPD2 в самой статье.
1) Если в Си++ нужен логический тип данных, то следует использовать bool. Это стандартно, чётко, ясно, всем понятно.
2) В учебниках про Windows любят писать про тип BOOL в силу исторических причин. Многие функции из WinAPI возвращают или принимают тип BOOL. Который на самом деле int. Причины этого думаю понятны.
3) Я не понял причину вывода, что нужно использовать BOOLEAN.
2) В учебниках про Windows любят писать про тип BOOL в силу исторических причин. Многие функции из WinAPI возвращают или принимают тип BOOL. Который на самом деле int. Причины этого думаю понятны.
3) Я не понял причину вывода, что нужно использовать BOOLEAN.
А интересно, будет разница в коде и если да, то в скорости работы с BOOL и bool.
1 и 2 пока не буду отвечать. А по пункту 3 — причина простая, с BOOLEAN все работает, а с BOOL нет. Поэтому и нужно использовать BOOLEAN.
С bool всё тоже будет работать. Так зачем лишние сущности? Вот что такое bool я знаю. А увидев BOOLEAN мне возможно придется идти смотреть его объявление.
И как всегда язык С++ путают с VisualC++ и подключенными виндовыми библиотеками… Я не раз сталкивался с людьми, которые искренне удивлялись, когда узнавали, что в языках С/С++ нету типов WORD и BYTE или функций вроде CreateFile.
Нет языка Visual C++, это IDE. Все, что вы указали (WORD, BYTE, CreateFile) — это из Win API, никакого отношения к языку не имеет. А то, что люди забывают какие типы/ф-ии из языка, а какие из подключаемых библиотек — это распространенная проблема не только у программистов С++ под виндоуз.
Стандарт не читал. Мой g++ говорит:
ошибка: нет декларации «BOOLEAN» в этой области видимости
ошибка: нет декларации «BOOL» в этой области видимости
Так что юзаем bool и не выпендриваемся.
ошибка: нет декларации «BOOLEAN» в этой области видимости
ошибка: нет декларации «BOOL» в этой области видимости
Так что юзаем bool и не выпендриваемся.
По поводу UPD3, какие оптимизации использовались?
Оптимизации не использовались, но по ассемблерному коду видно, что Borland Builder при возврате false делает xor eax,eax и ошибки нет.
А на Visual Studio, повторюсь, делается mov al,0 и ошибка в том, что старшие биты eax не очищены.
А на Visual Studio, повторюсь, делается mov al,0 и ошибка в том, что старшие биты eax не очищены.
А ошибка на самом деле в строке
CallBack IsEqual = (CallBack)CompareInt;
Привели к совершенно другому типу, ещё и удивляются, что не работает. Сигнатуру надо соблюдать. Если CallBack объявлен как BOOL (*)(int,int), то и функция должна иметь такой же прототип. Если нет — пишем обёртку и не надеемся на авось.
CallBack IsEqual = (CallBack)CompareInt;
Привели к совершенно другому типу, ещё и удивляются, что не работает. Сигнатуру надо соблюдать. Если CallBack объявлен как BOOL (*)(int,int), то и функция должна иметь такой же прототип. Если нет — пишем обёртку и не надеемся на авось.
Немного истории. На самом деле — данный пример был взят из большого проекта для Windows, где вся команда пользовалась BOOL и не знала проблем. Но тут к проекту подключился один деятель, изначально он писал (да и сейчас пишет) на C#, а там, как известно, применяется только bool (маленькими буквами). Он-то и написал фунцию сравнения через возврат bool.
Мало того, функция 100% работает для чисел менее 0x100, потому что такие числа «не загрязняют» старшие биты регистра eax. Мало того, при сборке компилятор на это никак не ругнулся. Мало того, главный по коммитам (ведущий программист) видел, но пропустил этот код, потому что подумал, что BOOL это bool. Мало того, функция успела полгода пролежать в svn без изменений и все решили, что ошибки там быть не может. А когда я продемонстрировал ошибку, в проекте сразу сделали апперкейс всем bool/false/true, что на мой взгляд, сродни харакири, потому что проблема глубже, чем это кажется на первый взгляд.
А потом все удивляются, что фобос-грунты падают. Печаль.
Мало того, функция 100% работает для чисел менее 0x100, потому что такие числа «не загрязняют» старшие биты регистра eax. Мало того, при сборке компилятор на это никак не ругнулся. Мало того, главный по коммитам (ведущий программист) видел, но пропустил этот код, потому что подумал, что BOOL это bool. Мало того, функция успела полгода пролежать в svn без изменений и все решили, что ошибки там быть не может. А когда я продемонстрировал ошибку, в проекте сразу сделали апперкейс всем bool/false/true, что на мой взгляд, сродни харакири, потому что проблема глубже, чем это кажется на первый взгляд.
А потом все удивляются, что фобос-грунты падают. Печаль.
Вообще-то, заменить bool на BOOL это тоже неверное решение. Как отметили, настоящая ошибка в том, что используется явное приведение типа. Это и есть причина ошибки, а не BOOL. Как это сделать? Можно приобрести PVS-Studio и просмотреть все явные касты в стиле Си.
А что у вас кстати с 64-битной вресией? Есть или планируете? А то вот где забавно с типами и явным приведением… ;)
А что у вас кстати с 64-битной вресией? Есть или планируете? А то вот где забавно с типами и явным приведением… ;)
64-битная версия готова! Как ни странно, версия для Windows пошла сразу же после перекомпиляции.
Если не считать небольших костылей в RegOpenKeyEx по работе с реестром:
Или несколько костылей в связи с изменением (улучшением) MFC:
А вот с версией для x64 Linux пришлось изрядно повозиться, основная проблема была в том, что родной тип long там 64-х битовый. Поэтому все упоминания long в программе пришлось апперкейсить в LONG и далее превращать их принудительно в int:
Совсем избавиться от long, превратив его везде в int нельзя, потому что нам нужна поддержка embedded процессоров, а они сплошь 16-ти битовые, то есть там sizeof(int)=2, что недостаточно, нам нужна точность 4 байта.
Если не считать небольших костылей в RegOpenKeyEx по работе с реестром:
#ifdef WIN64
KEY_WOW64_32KEY
#else
KEY_READ
#endif
Или несколько костылей в связи с изменением (улучшением) MFC:
#ifdef WIN64
void CJoinDialog::OnTimer(UINT_PTR nIDEvent)
#else
void CJoinDialog::OnTimer(UINT nIDEvent)
#endif
А вот с версией для x64 Linux пришлось изрядно повозиться, основная проблема была в том, что родной тип long там 64-х битовый. Поэтому все упоминания long в программе пришлось апперкейсить в LONG и далее превращать их принудительно в int:
#ifdef LINUX_x64
typedef int LONG;
#else
typedef long int LONG;
#endif
Совсем избавиться от long, превратив его везде в int нельзя, потому что нам нужна поддержка embedded процессоров, а они сплошь 16-ти битовые, то есть там sizeof(int)=2, что недостаточно, нам нужна точность 4 байта.
а про типв size_t и ptrdiff_t Вы, я так понимаю не слышали?
И причем здесь это? Нужен был тип, который гарантированно дает sizeof=4, для хранения там данных, передаваемых из девайса, не указателей и не индексов массивов. Неважно, знаковый он будет или беззнаковый.
На Java, кстати, там что 32 что 64 бита — int всегда один и тот же — знаковый 4 байта.
И long там всегда один и тот же, знаковый 8 байт. Что хорошо.
А на Си развели демократию, понимаешь.
На Java, кстати, там что 32 что 64 бита — int всегда один и тот же — знаковый 4 байта.
И long там всегда один и тот же, знаковый 8 байт. Что хорошо.
А на Си развели демократию, понимаешь.
#ifdef WIN64
void CJoinDialog::OnTimer(UINT_PTR nIDEvent)
#else
void CJoinDialog::OnTimer(UINT nIDEvent)
#endif
Эээ… А зачем так? Почему не просто:
void CJoinDialog::OnTimer(UINT_PTR nIDEvent)
P.S. Я был бы поаккуратнее, говоря, что 64-битная пограмма пошла сразу после перекомпиляция :-). Вся подлянка, что часто только кажется, что работает. Приведу только один красивый пример: www.viva64.com/ru/b/0033/
>А потом все удивляются, что фобос-грунты падают. Печаль.
Я не удивлен, что в итоге выяснилось, что в падении Фобос-Грунта виноват программист
Я не удивлен, что в итоге выяснилось, что в падении Фобос-Грунта виноват программист
Жду от автора новых «откровений» про ошибки компиляции следующего выражения:
std::numeric_limits::max()
при подключенном <windows.h> и почему они лечатся макросом NOMINMAX
std::numeric_limits::max()
при подключенном <windows.h> и почему они лечатся макросом NOMINMAX
А откровения, чем отличается
И почему там в одном месте кавычки, в другом треугольные скобки, да еще .h иногда отсутствует?
Кстати, это неплохой вопрос на собеседовании, уверен, что 90% на него не ответит — просто так принято.
Так и в нашем случае — писать BOOL это в среде Windows API общепринято, и ничего тут не поделать.
#include <iostream>
#include <windows.h>
#include "stdafx.h"
случайно не интересны? И почему там в одном месте кавычки, в другом треугольные скобки, да еще .h иногда отсутствует?
Кстати, это неплохой вопрос на собеседовании, уверен, что 90% на него не ответит — просто так принято.
Так и в нашем случае — писать BOOL это в среде Windows API общепринято, и ничего тут не поделать.
Вы явно не в теме. Кавычки тут не при чем.
Объясните следующий пример:
И «писать BOOL это в среде Windows API общепринято» не факт… я не пишу.
Все, что выходит за рамки прямых вызовов WinAPI у меня на независимых типах uint8_t/sint16_t/size_t и т.п.
Объясните следующий пример:
#include <limits>
#include <iostream>
#include <windows.h>
int main( int argc, char *argv[] )
{
int _imax = std::numeric_limits< int >::max();
std::cout << "int max = " << _imax << std::endl;
return 0;
}
1>c:\project\source\tests\test_limith\main.cpp(9): warning C4003: not enough actual parameters for macro 'max'
1>c:\project\source\tests\test_limith\main.cpp(9): error C2589: '(': illegal token on right side of '::'
1>c:\project\source\tests\test_limith\main.cpp(9): error C2059: syntax error: '::'
И «писать BOOL это в среде Windows API общепринято» не факт… я не пишу.
Все, что выходит за рамки прямых вызовов WinAPI у меня на независимых типах uint8_t/sint16_t/size_t и т.п.
Ваш пример компилируется ошибкой, потому что в windows.h где-то есть #define max(a,b)
и поэтому препроцессор «портит» код. Лечится так:
и поэтому препроцессор «портит» код. Лечится так:
#include <limits>
#include <iostream>
#include <windows.h>
#undef max
Ну или так, как Вы указали выше, через NOMINMAX, он тоже отключает #define max#undef max
Я бы за это Вас по пальцам… указкой… деревянной… больно… до осознания!
1. Этот пример как раз из той же серии, что и Ваша статья — давно известно и разобрано по косточкам. К тому же специфично. BOOL и BOOLEAN равнонеправильны. В С++ правильно — bool, это встроенный тип.
LONG, UINT_PTR, BOOL — это не типы С++, это типы WinAPI и если их используете, то обязаны знать, что они из себя представляют. А значит «открытия» типа typedef int BOOL говорят как раз о незнании используемого инструмента и нежелании пользоваться поисковиками.
2. Кроме max() там еще и min() создается. Будете на каждый макрос undef делать?
3. Есть еще одно красивое решение, без макросов.
За снобизм извиняюсь, но порой без него не обойтись.
Самое грамотное решение такое — все, что связано с windows.h, все функции работы с WinAPI, нужно вынести в отдельный исходный файл своего проекта и написать к ним отдельный интерфейс. Потому что мешать в кучу старое и новое — неправильно. Так сказать, мухи отдельно, котлеты отдельно.
Я думаю, именно это Вы и имели в виду в пункте 3.
Я думаю, именно это Вы и имели в виду в пункте 3.
Вам сейчас подскажут, что CreateThread еще одно мегазло, нужно использовать _beginthread(ex), если этот поток потом использует CRT иначе тоже будет happy debugging.
Вы решили запустить PVS-Studio? :)
Я просто внимательно слежу за Вашими статьями и в одной из них было на эту тему.
Я потом специально посмотрел, а как у нас? Оказалось, у нас используется AfxBeginThread (которая из MFC), а она в итоге работает через _beginthreadex. Пронесло, подумал Штирлиц.
Я потом специально посмотрел, а как у нас? Оказалось, у нас используется AfxBeginThread (которая из MFC), а она в итоге работает через _beginthreadex. Пронесло, подумал Штирлиц.
Недавно я тоже разместил статью про булевы типы (но в C#), поэтому, хотя тут обсуждение и давнее, хотелось быт поднять тему.
Вы провели хороший анализ, но я рекомендую придерживаться следующего:
1. Нативный булевский тип в C++ — это bool. Всегда используйте его с своих проектах.
Если пишите на чистом C, не поддерживающим bool, используйте в качество булева типа обычный int.
Считаю большой ошибкой использовать в своей вычислительной логике вместо стандартных типов языка типы, определенные как синонимы других типов в заголовочных файлах Windows SDK: BOOL, BOOLEAN, DWORD и т.д.
Если ваша программа может быть открыта как COM-объект, то стандартных типов данных в этом случае тоже хватит.
2. Что касается BOOL и BOOLEAN в WinAPI, то «правильный» тип — BOOL (C-Style: 32-битное целое, 0 — False, не-0 — True).
А тип BOOLEAN это Pascal-style, оставшийся в WinAPI в функциях, разработанных, когда в ранних версиях Windows API разрабатывался на паскале (1 байт, 0 — False, 1 — True).
Какой тип использовать в этом случае — определяется декларацией вызываемой функции WinAPI.
Если вы как-то в дальнейшей логике используете булевы значения, возвращенные API, то такие значение лучше сразу перевести в значения стандартного типа bool.
Вы провели хороший анализ, но я рекомендую придерживаться следующего:
1. Нативный булевский тип в C++ — это bool. Всегда используйте его с своих проектах.
Если пишите на чистом C, не поддерживающим bool, используйте в качество булева типа обычный int.
Считаю большой ошибкой использовать в своей вычислительной логике вместо стандартных типов языка типы, определенные как синонимы других типов в заголовочных файлах Windows SDK: BOOL, BOOLEAN, DWORD и т.д.
Если ваша программа может быть открыта как COM-объект, то стандартных типов данных в этом случае тоже хватит.
2. Что касается BOOL и BOOLEAN в WinAPI, то «правильный» тип — BOOL (C-Style: 32-битное целое, 0 — False, не-0 — True).
А тип BOOLEAN это Pascal-style, оставшийся в WinAPI в функциях, разработанных, когда в ранних версиях Windows API разрабатывался на паскале (1 байт, 0 — False, 1 — True).
Какой тип использовать в этом случае — определяется декларацией вызываемой функции WinAPI.
Если вы как-то в дальнейшей логике используете булевы значения, возвращенные API, то такие значение лучше сразу перевести в значения стандартного типа bool.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
BOOL или BOOLEAN — вот в чем вопрос?