Здравствуйте, so5team, Вы писали:
S>Гораздо важнее другой вопрос: когда библиотека поставляется в бинарном виде, как быть тогда? На ваш же вопрос ответ прост: поддержка С++11.
Ну, крупные игроки предоставляют версии под разные платформы. Кустарные мастерские (например, Кэнон: http://community.usa.canon.com/t5/Office-Printers/setting-up-ImageFormula-DR-C225-on-Linux/m-p/169967 ) поставляют исподники. Кстати, в данном случае налицо преимущество одного бесконечного hpp, когда тестировщики не могут сосредоточиться.
mng>>И что им делать, если их текущий сборщик не переварит всех нововведений?
S>Искать что-нибудь более подходящее для себя. Написать свое. Заплатить за адаптацию под свои условия. Переделать самостоятельно. С исходным текстом можно делать все, что угодно.
Ещё вариант: использовать в библиотечном коде старые стандарты. Пусть не модно, зато благодарность пользователей не будет знать границ.
mgu>>Да нет, к подходу -- налицо излишняя многословность.
S>Разница только в количестве строк.
Вам никогда не давали в качестве тестового задания посчитать количество слов в файле?
mgu>> Против инкрементов у вас же возражений нет?
S>Инкременты откуда взялись?
Из вашего кода. Ведь можно и без них обойтись.
S>>>К коду AlexGin сходу можно предъявлять претензии гораздо более серьезные.
mgu>>Например?
S>Да вот, первый же cpp-файл в репозитории. И сразу же: S>
S>bool SMSContentGreater(CSMSContent* pSMS1, CSMSContent* pSMS2) // Global function for std::sort
S>{
S> CTime time1, time2;
S> SYSTEMTIME st1, st2;
S> if (pSMS1->GetSMSTime().GetAsSystemTime(st1))
S> time1 = CTime(st1);
S> if (pSMS2->GetSMSTime().GetAsSystemTime(st2))
S> time2 = CTime(st2);
S> time_t t1 = time1.GetTime();
S> time_t t2 = time2.GetTime();
S> if (t1 > t2)
S> return true;
S> else
S> return false;
S>}
S>
.
S>Однако, особая пикантность ситуации состоит в том, что у CTime уже есть оператор "меньше". Посему непонятно, а зачем вообще нужна еще одна трансформация из CTime в time_t. Более того, очень похоже, что GetSMSTime уже возвращает объект типа CTime, у которого затем дергается GetAsSystemTime() дабы получить SYSTEMTIME. А затем из SYSTEMTIME вновь конструируется CTime... Чой-то неведомое.
Согласен.
S>Поля m_pTXBuff и m_pRXBuff объявлены в самом классе CATCmdDevice. Следовательно, занулять их в деструкторе после освобождения памяти смысла нет.
А и правда.
S>Опять же, на счет m_pTXBuff и m_pRXBuff: почему идет ручное управление памятью? Почему нельзя было воспользоваться std::vector (рекомендуемый путь со времен C++98)? Или вообще почему не сделать m_TXBuff и m_RXBuff обычными C-шными массивами?
S>Далее: S>
S>Что за проверка m_pSerial != NULL после new? Неужели автор транслирует свой код с запретом C++ных исключений на уровне ключей компилятора? Если даже он делает именно так, то где ветка else у этого if-а? А то ведь получается, что при запрещенных исключениях у него нет обработки ошибки выделения памяти (даже если в m_pSerial окажется 0, функция OpenSerial все равно вернет 0 в качестве признака успешного завершения). Если же исключения не запрещены, то данная проверка не имеет смысла, т.к. при недостатке памяти new бросит bad_alloc.
Угу, плюс подобные проверки вызывают беременность кода (его дюже сдвигает вправо, лучше на нет и суда нет, а не оборачивать успешную ветку) и таки-да, опять return ... else.
S>И это, повторюсь, всего лишь беглый просмотр всего лишь первого файла.
S>Несомненное достоинство данного кода -- это возможность в нем разобраться. Но общее впечатление, что его писал вчерашний студент, который только-только начал изучать язык C++.
Здравствуйте, AlexGin, Вы писали:
AG>Некоторые "эффективные менеджеры" всё время кричат, что C++ отжил AG>Так как я придерживаюсь абсолютно противоположной точки зрения
Вот отсюда и идут проблемы.
Вы можете придерживаться любой точки зрения, но если она идёт вразрез с мнением работодателя, то договориться вам будет сложно.
Я писал на С и С++ лет 5 примерно, на аутсорсе. К концу 2000-х я уже отчётливо видел, что десктоп на С и С++ быстро исчезает. Уже тогда большинство наших заказчиков С и С++ интересовал больше в плане сетевых приложений.
В мой профиль LinkedIn время от времени стучатся рекрутеры с разными предложениями. Так вот, навскидку, из 10 предлагаемых мне вакансий в среднем 3 это .net, 6 JEE и только одна C++.
Здравствуйте, AlexGin, Вы писали:
AG>[...]Для пользователя
Пользователь видит HTML. HTML нынче довольно хорошо развит и уже почти не вызывает раздражения.
Более того, HTML нынче это не только вёб, это уже и десктоп. Посмотрите на Visual Studio Code (это не сама visual studio, это совсем другой проект от того же MS). Это десктопное приложение, написанное на HTML.
AG>Но, тем не менее, остаюсь при своей точке зрения
Наши с вами точки зрения имеют меньшее значение, чем точка зрения тех, кто инвестирует в создание продукта. А инвесторы нынче любят HTML куда больше чем C++.
Здравствуйте, so5team, Вы писали:
S>>>Во-первых, фраза "динамические члены класса" применительно к C++ режет слух. Что еще больше утверждает в мысли, что вы пытаетесь судить о том, в чем не разбираетесь.
mgu>>Ваш термин для не статических членов класса в С++?
S>Поля, атрибуты, не статические члены класса.
Посмотрел, с последним соглашусь, с первым -- нет: "C++ (like Java) calls them static fields and static methods." http://www.uic.edu/classes/mcs/mcs451/f05/transparencies/sec4.3b.pdf
S>>>Т.е. вы не разобрались с тем, что делает код и почему он это делает именно так, но претензии предъявили?
mgu>>Да.
S>Ok. На этом все ваши аппеляции якобы к имеющемуся у вас опыту разработки софта идут прямиком в /dev/null.
Разделяю ваш праведный гнев с одной оговоркой: код отдельно взятой функции должен быть самодостаточен для её понимания.
>> Кто виноват в проблеме -- тупой пользователь или напускающий туман и опускающий комментарии разработчик?
S>Пользователи не смотрят в код. Разработчики смотрят в код и видят комментарии: S>
/*!
S> * \brief Execute all active timers in the sublist.
S> *
S> * Object is unlocked and locked back after sublist processing.
S> */
S> template< class UNIQUE_LOCK >
S> void
S> exec_actions(
S> //! Object lock.
S> //! This lock will be unlocked before execution of actions
S> //! and locked back after.
S> UNIQUE_LOCK & lock,
S> //! Head of execution list.
S> //! Cannot be nullptr.
S> timer_type * head )
S> {
S> lock.unlock();
S> while( head )
S>
S>Данных комментариев может быть явно недостаточно, если перед этим не изучить предметную область хотя бы на уровне чтения документации к библиотеке. Дабы возникло понимание как работают конкретные таймерные механизмы.
Да читал я ваши комментарии, они, грубо говоря, объясняют, что делается (это-то и так ясно по коду), но непонятно, почему так. Или вот:
//! Head of execution list.
//! Cannot be nullptr.
timer_type * head )
{
lock.unlock();
while( head )
А почему "Cannot be nullptr"?
S>>>эта операция может быть длительной (особенно если таймеров сотни тысяч), в это время нужно позволить параллельно работающим нитям работать с общим списком таймеров (выставлять новые или отменять существующие);
mgu>>То есть в то время как всё прогрессивное человечество пишет синглтоны с двумя if-ами, вы позволяете внешним акторам хулиганить? Всё-таки привычнее видеть операции должным образом изолированными.
S>Сами-то поняли, что сказали?
А вы?
// Status of timer can be changed. So it must be checked
// just before execution. If timer is waiting for
// deregistration it must not be executed.
if( timer_status::wait_for_execution == head->m_status )
head->m_action();
"Сотни тысяч таймеров" вам могут преподнести зловредную ошибку на последней строчке.
S>>>А известно ли вам, что в C++ не принято выбрасывать исключения из деструкторов?
mgu>>Конечно, и не только в С++, это же любимые вопросы на интервью: по Джаве на тему повторного вызова деструктора, а по С# -- про сожительство деструктора и финализатора. Но под "очисткой" я понимал не только разрушение.
S>Только вам известно, что вы понимали. Но явно не то, о чем идет речь.
The destructor is commonly used to "clean up" when an object is no longer necessary.
При чём здесь выбрасывание исключений?
S>>>Поскольку если такое исключение выскочит при раскрутке стека из-за ранее возникшего исключения, то сам ран-тайм тупо завет std::terminate (суть std::abort()). И это считается нормально: пользователь нарушил контракт, получил по рукам, ибо обеспечить восстановление после такого нарушения контракта возможно далеко не всегда. Так же и здесь -- нельзя выпускать исключения из callback-а. Поскольку некому его будет обрабатывать на отдельной рабочей нити, на которой и происходят вызовы callback-ов.
mgu>>Не выпускайте, ещё раз: выставляйте локальный флаг и выходите из while.
S>Объясню еще раз. Есть отдельная рабочая нить, на которой вызываются заданные пользователем callback-и. Тот while, который вы видели в коде -- это один из циклов внутри данной нити. Callback-и не должны выбрасывать исключения, т.к. на данном контексте с ними сделать ничего нельзя. Соответственно, если пользователь нарушил контракт и выбросил исключение из callback-а, то возможности что-то с этим сделать нет. Поэтому, как результат нарушения контракта, вызывается std::abort.
А что, в С++ нет способа обратиться к основному потоку? Необязательно выбрасывать исключение, но можно же передать информацию.
S>Все это становится понятно после хоть сколько нибудь серьезного погружения в тему. Чего вы не сделали, но выдвинули претензию к коду, в котором вам что-то не понравилось без понимания сути происходящего.
А теперь представьте, что я "пользователь" вашего кода, у меня он чуть-чуть не компилируется, и я хочу быстренько подправить проблемные места. Вы предлагаете мне "погрузиться в тему"? В таком случае я и свой код напишу, под мои конкретные нужды.
mgu>>А чем dll вам не угодили? Для любителей hardcore можно предоставлять версии с отладочной информацией. Или фишка в компилировании под конкретные платформы?
S>Вы вообще в курсе, что кроме Windows с dll-ками есть еще туева хуча платформ? Как программных, так и аппаратных? Или вы думаете, что скомпилированные Visual C++ бинарные dll-ки переносятся с платформы на платформу так же, как это происходит с Java байт-кодом?
Мой вопрос подразумевал знание наличия других платформ. Я этим никогда не занимался, посмотрел и обнаружил, что конвертаторов с платформы на платформу нет, странно, вроде бы насущная и решаемая задача. Но с кодом тоже не всё так просто: требуется подправлять под конкретику.
S>>>Кросс-платформенных и широкораспространненых менеджеров пакетов в C++ пока не завезли. Это тоже к вопросу опыта и его релевантности современным реалиям ИТ-шного рынка.
mgu>>Не смог согласовать первое предложение со вторым.
S>Ничего удивительного. Вы пытаетесь рассуждать о теме, в которой не разбираетесь.
Да, рассуждать, спрашивать, сомневаться, а вовсе не утверждать. Это зазорно?
Здравствуйте, mgu, Вы писали:
mgu>Во-первых, крайне необычно сначала разблокировать, а затем блокировать [...]
Вставлю свои пять копеек.
В приведённых вами кусках кода я никаких явных проблем не вижу. А вот код AlexGin, и в самом деле, вызывает некоторые вопросы. Вот тут, к примеру:
if (m_pSerial)
delete m_pSerial;
m_pSerial = new CSerial();
if (m_pSerial != NULL)
....
Сразу возникает две мысли. Во-первых, delete не обнуляет указатель. Впрочем, тут надо бы посмотреть на документацию к тому компилятору, который использовался в том проекте, но всяко надёжнее и безопаснее было бы добавить "m_pSerial = null". Ибо если какой-то компилятор обнуляет, то другой может не обнулять. Иначе рискуем вызвать delete для уже уничтоженного объекта.
А вот следующая строка напрямую вызывает вопросы о знании стандарта С++. Я помню, что какие-то древние версии компилятора возвращали null в случае проблем при аллокации памяти. Но современные компиляторы давно уже выбрасывают исключение, а не возвращают null. Можно явно запросить то старое поведение таким образом:
m_pSerial = new(nothrow) CSerial();
Может быть это особенности того конкретного проекта и компилятора, которым оно собирается, но вот сходу выглядит как незнание стандарта.
А вот тут функция CNewSamsung::SendSmsMass на ~240 выглядит неимоверно раздутой.
Здравствуйте, Artem Korneev, Вы писали:
mgu>>Он включён в дополнительные возможности.
AK>Это не дополнительные возможности. Это C++/.net, который совсем не то же самое, что просто C++.
Да, с этим уже разобрались, я просто привёл неудачный пример заимствования С++11-м из других языков.
Здравствуйте, Artem Korneev, Вы писали:
AK>Может быть это особенности того конкретного проекта и компилятора, которым оно собирается, но вот сходу выглядит как незнание стандарта. AK>А вот тут функция CNewSamsung::SendSmsMass на ~240 выглядит неимоверно раздутой.
Всё относительно, мне попадались функции на несколько тысяч строк, там был не просто switch, а целая АТС.
AK>Сразу возникает две мысли. Во-первых, delete не обнуляет указатель. Впрочем, тут надо бы посмотреть на документацию к тому компилятору, который использовался в том проекте, но всяко надёжнее и безопаснее было бы добавить "m_pSerial = null". Ибо если какой-то компилятор обнуляет, то другой может не обнулять. Иначе рискуем вызвать delete для уже уничтоженного объекта.
В данном случае после delete идет сразу new, поэтому занулять указатель бессмысленно(ему сразу же присваивается значение).
Здравствуйте, Artem Korneev, Вы писали:
AK>В мой профиль LinkedIn время от времени стучатся рекрутеры с разными предложениями. Так вот, навскидку, из 10 предлагаемых мне вакансий в среднем 3 это .net, 6 JEE и только одна C++.
интересная статистика
если не секрет а с Python были предложения
Здравствуйте, mgu, Вы писали:
mgu>Ещё вариант: использовать в библиотечном коде старые стандарты. Пусть не модно, зато благодарность пользователей не будет знать границ.
Это не имеет отношения к обсуждению качества кода.
mgu>>>Да нет, к подходу -- налицо излишняя многословность.
S>>Разница только в количестве строк.
mgu> Вам никогда не давали в качестве тестового задания посчитать количество слов в файле?
Понятно. Т.е. вы видите принципиальную разницу между:
if(demands < 3)
demands = 1;
else
demands -= 2;
demands = demands < 3 ? 1 : demands - 2;
Ну Окай.
mgu>Н-да... Но бывает гораздо хуже!
Наверняка бывает. Только если согласиться с точкой зрения AlexGin о том, что рынок труда в Минске сокращается, то это ведет к усилению конкуренции для оставшихся на рынке разработчиков. Соответственно, чем хуже код, тем меньше шансов найти работу. Просто по уровню профессиональных качеств.
Здравствуйте, mgu, Вы писали:
mgu>>>Ваш термин для не статических членов класса в С++?
S>>Поля, атрибуты, не статические члены класса.
mgu>Посмотрел, с последним соглашусь, с первым -- нет: "C++ (like Java) calls them static fields and static methods." mgu>http://www.uic.edu/classes/mcs/mcs451/f05/transparencies/sec4.3b.pdf
Мы все еще говорим про не статические члены классов в C++?
mgu>Да читал я ваши комментарии, они, грубо говоря, объясняют, что делается (это-то и так ясно по коду), но непонятно, почему так.
mgu>//! Head of execution list.
mgu>//! Cannot be nullptr.
mgu>timer_type * head )
mgu>{
mgu>lock.unlock();
mgu>while( head )
mgu>
mgu>А почему "Cannot be nullptr"?
Потому что этот метод должен вызываться только для непустых списков. Если список пуст, то методы exect_actions и utilize_exec_list не должны вызываться и не вызываются.
S>>>>эта операция может быть длительной (особенно если таймеров сотни тысяч), в это время нужно позволить параллельно работающим нитям работать с общим списком таймеров (выставлять новые или отменять существующие);
mgu>>>То есть в то время как всё прогрессивное человечество пишет синглтоны с двумя if-ами, вы позволяете внешним акторам хулиганить? Всё-таки привычнее видеть операции должным образом изолированными.
S>>Сами-то поняли, что сказали?
mgu>А вы?
До сих пор мне было понятно все, что я здесь писал. И, местами, было понятно то, что пишете вы. Но вот в данном случае ничего не понял. Какие синглетоны, что за "должным образом изолированными", какое отношение это все имеет к параллельной модификации списка таймеров...
mgu>
mgu>// Status of timer can be changed. So it must be checked
mgu>// just before execution. If timer is waiting for
mgu>// deregistration it must not be executed.
mgu>if( timer_status::wait_for_execution == head->m_status )
head->>m_action();
mgu>
mgu>"Сотни тысяч таймеров" вам могут преподнести зловредную ошибку на последней строчке.
Какую же?
mgu>При чём здесь выбрасывание исключений?
Подучите таки матчасть. В C++ не принято бросать исключения из деструкторов. В некоторых случаях это ведет к вызову std::terminate, на что пользователь повлиять никак не может. Выбрасывание исключений из callback-ов объектов таймеров очень напоминает ситуацию с выбрасыванием исключений из деструкторов. И шансов восстановиться после этого приблизительно столько же.
mgu>А что, в С++ нет способа обратиться к основному потоку? Необязательно выбрасывать исключение, но можно же передать информацию.
А причем здесь основной поток вообще? На C++ есть масса программ, в которых в основном потоке только первоначальная инициализация, а затем все действия выполняются на десятках, а то и сотнях рабочих потоках. Каждый из которых может выставлять таймерные заявки. Если какая-то из этих заявок нарушила контракт, то чем здесь поможет основной поток?
Вообще, складывается ощущение, что ваше знакомство с C++ ограничивается тесным мирком однопоточных GUI приложений для Windows.
S>>Все это становится понятно после хоть сколько нибудь серьезного погружения в тему. Чего вы не сделали, но выдвинули претензию к коду, в котором вам что-то не понравилось без понимания сути происходящего.
mgu>А теперь представьте, что я "пользователь" вашего кода, у меня он чуть-чуть не компилируется, и я хочу быстренько подправить проблемные места. Вы предлагаете мне "погрузиться в тему"? В таком случае я и свой код напишу, под мои конкретные нужды.
Ну т.е. если вам попадает в руки библиотека с реализацией B+ деревьев и вы хотите чуть-чуть подправить проблемные места в ней, то вы не хотите "погружаться в тему"? А если это будет еще и lock-free реализация с вариацией hazard pointers, то вы хотите, чтобы код был написан так, чтобы одного взгляда на него хватило для понимания принципов его работы? И вы свой напишете под свои конкретные нужды?
Но Окай еще раз.
mgu>Да, рассуждать, спрашивать, сомневаться, а вовсе не утверждать. Это зазорно?
Если вы выкатываете замечания к коду не понимая этого кода, то не просто зазорно.
S>В данном случае после delete идет сразу new, поэтому занулять указатель бессмысленно(ему сразу же присваивается значение).
Если б это было единственным местом, где m_pSerial может быть удалён и обнулён, то, согласитесь, проверять его на null тоже было бы бессмысленно.
Впрочем, есть ещё вариант что он может быть не создан вообще, а просто приравнен к null с самого начала.
Здравствуйте, sergey2b, Вы писали:
AK>>В мой профиль LinkedIn время от времени стучатся рекрутеры с разными предложениями. Так вот, навскидку, из 10 предлагаемых мне вакансий в среднем 3 это .net, 6 JEE и только одна C++. S>интересная статистика
У меня в профиле LinkedIn написано что я C++ разработчик. Плюс, там ещё два buzz-word'а — Map/Reduce и BigData. Вот заметная часть предлагаемых вакансий касались BigData программизма на Java. При том, что опыт в самой Java у меня весьма скромный.
Рекрутеры стучатся, в среднем, два-три раза за месяц. Не очень часто, но за 4 года моего проживания в заграницах кое-какая статистика уже скопилась.
S>если не секрет а с Python были предложения
Да, были. Но не много. Два или три за всё время. Но оно и понятно — python у меня в резюме упоминается вскользь, я на нём какие-то вспомогательные скрипты ваял в нескольких проектах. То, что предлагалось на питоне, всегда имело отношение к вёбу — Django и всё такое.
Один раз предлагали вакансию на Erlang, что само по себе звучало весьма интересно, но прыгать с места на место из-за этого не хотелось.
Здравствуйте, steep8, Вы писали:
AK>>Сразу возникает две мысли. Во-первых, delete не обнуляет указатель. Впрочем, тут надо бы посмотреть на документацию к тому компилятору, который использовался в том проекте, но всяко надёжнее и безопаснее было бы добавить "m_pSerial = null". Ибо если какой-то компилятор обнуляет, то другой может не обнулять. Иначе рискуем вызвать delete для уже уничтоженного объекта.
S>В данном случае после delete идет сразу new, поэтому занулять указатель бессмысленно(ему сразу же присваивается значение).
Если предположить, что new выполняется как new(nothrow), то это так.
Но в таком случае — уже говорили — действия типа "а пофиг, что не выделили, всё равно передаём OK", мягко говоря, смущают.
Если же new вызывает исключение, то в случае исключения будет в m_pSerial значение старого указателя на уже освобождённую память и разрушенный объект, и дальше всё зависит от того, что делается по этому исключению. Если пытаются использовать объект ещё раз, то последствия непредсказуемы.
Именно поэтому подход "освободил => занули" имеет весомый смысл везде, кроме деструкторов и других мест, где использовать эти переменные уже некому.
Здравствуйте, mgu, Вы писали:
mgu>Если один из инженеров скажет, что он в одиночку клепает самолёт, тогда возникнут.
Главный конструктор имеет полное право сказать нечто подобное.
Ужас. Столько гадостей в сторону одной из немногих нормальных компаний России. Зачем? Если не нравится — не апплайтесь
А по поводу собеседования — имеют полное право задавать те вопросы, которые считают нужными.
Здравствуйте, itslave, Вы писали:
I>Здравствуйте, mgu, Вы писали:
mgu>>Если один из инженеров скажет, что он в одиночку клепает самолёт, тогда возникнут. I>Главный конструктор имеет полное право сказать нечто подобное.