Как стать автором
Обновить

Комментарии 6

Странно, что такая отличная серия переводов так мало плюсуется. На хабре, по-моему, вообще ещё не было ни одного доведённого до конца перевода целой книги. А тут по всему видно, что дело будет до конца доведено, круто.
Рука не поднимается плюс ставить по причинам:

  1. Макросы MEM_FN. Перевод для начинающих, ну и не надо начинающих программистов приучать к макросам. Это же C++.
  2. Форматирование исходных текстов ужасно. Я понимаю, что это нудно и неудобно… но код должен быть оформлен красиво.
  3. Ну и содержимое на столько простое и очевидное, что лично я не вижу смысла в этом переводе. Документация у boost не на столько хороша, как у Qt, но все же имеется.


Разумеется минус я тоже не стал ставить.
1) расскажите пожалуйста подробнее, что плохого в таких макросах:
#define MEM_FN(x) boost::bind(&self_type::x, shared_from_this())
#define MEM_FN1(x,y) boost::bind(&self_type::x, shared_from_this(),y)
#define MEM_FN2(x,y,z) boost::bind(&self_type::x, shared_from_this(),y,z)

Это же простые однострочники, при том безопасные, и даже контроль типов нигде не пропущен из-за простоты. Для реализации этого «по сиплюсплюсному» потребовался бы шаблон, который был бы гораздо толще и лишь помешал бы восприятию кода.
2) это тема вечного холивара, по мне так нормальное вполне, что конкретно Вам не понравилось? И да, прошу осознать, что это всё таки перевод.
3) no comment.
1. Плох сам факт использования макросов. Это как если бы вместо for/while использовать метку и goto, вроде такого:

    int i = 0;
label:
    // code
    if (++i < 100)
        goto label;


Казалось бы, ну что тут плохого? Код на C++, компилируется и работает без ошибок. Значит код правильный и использовать можно. Примерно такими же соображениями руководствовался программист, сделавший в где-то в WinAPI макрос с именем max и мне уже много лет приходится писать #ifdef max\n#undef max\n#endif. Чем именно плохи макросы, по моему, написано в любой книге по программированию на C++ и также дана рекомендация не использовать их. Но давайте я не буду учить вас программированию.

2. Тема для холивара — это какой стиль лучше. Тут же, откровенно говоря, стиля нет вообще. Код написан небрежно.

Вот например:

void on_read(const error_code & err, size_t bytes) 
{
    if ( err) stop();
    if ( !started() ) return;
    std::string msg(read_buffer_, bytes);
    if ( msg.find("login ") == 0) on_login(msg);
    else if ( msg.find("ping") == 0) on_ping();
    else if ( msg.find("ask_clients") == 0) on_clients();
}


и тут уже ниже

void on_clients() 
{
    std::string msg;
    for(array::const_iterator b =clients.begin(),e =clients.end(); b != e; ++b)
    msg += (*b)->username() + " ";
    do_write("clients " + msg + "\n");
}


Глядя на первый кусок уже и не кажется, что строка msg += (*b)->username() + " "; является телом цикла. Ведь даже отступа нет.

void read_answer()
{
    already_read_ = 0;
    read(sock_, buffer(buff_),
    boost::bind(&talk_to_svr::read_complete, this, _1, _2));
    process_msg();
}


Тут вообще аргумент функции на новой строке.

typedef std::vector<client_ptr> array;
array clients;


Вот зачем специально запутывать людей, создавая тип array, который на самом деле vector? Это ведь разные типы данных, один динамический массив, другой использует память на стеке. Кстати говоря, члены класса talk_to_client:

    char read_buffer_[max_msg];
    char write_buffer_[max_msg];


должны быть объявлены вот так:

    boost::array<char, max_msg> read_buffer_;
    boost::array<char, max_msg> write_buffer_;


Ну и все остальное в том же духе. Конечно кому-то кажется, что человек может писать так, как ему хочется. Использовать макросы и goto, привидение типов в стиле C, free и delete вместо RAII… Надо проработать программистом не один год и в разных командах, чтобы осознать важность этих правил.

И да, я вижу, что это перевод. И даже потрудился заглянуть в оригинал и выяснить, что макросы и такой вот нехороший код идут от туда, а не от переводчика. Но вопрос был совсем не в том, хорош перевод или нет. Я отвечал на комментарий объяснив причину, по которой я стал ставить плюс.
1. На макросы будет много ругани при компиляции если макрос с таким именем уже присутствует.
Примерно такими же соображениями руководствовался программист, сделавший в где-то в WinAPI макрос с именем max и мне уже много лет приходится писать #ifdef max\n#undef max\n#endif.

Вообще то WinAPI, как и огромное количество другого системного кода, это C, а не C++, а стало быть наличие там данных макросов — нормальная ситуация. И да, в ОС используются гораздо более серьёзные макросы, в том числе и глобальные. К слову, в С++ до сих пор не реализован даже if этапа компиляции, и это огромная проблема, зато реализованы монструозные конструкции sfinae на которых и предлагается что то делать по «сиплюсплюсному» для реализации парадигмы «if этапа компиляции».
2.
void on_read(...) — нормальный код, хотя если бы его писал я, то написал бы со скобками под каждым условием,
а вообще
как то так:
void on_read(const error_code & err, size_t bytes) 
{
    if (err)
    {
         stop();
    }
    else if (started())
    {
         const std::string msg(read_buffer_, bytes);
         static const char* login("login ");
         static const char* ping("ping");
         static const char* ask_clients("ask_clients");
         if (msg.compare(0, _countof(login) - 1, login) == 0)
         {
                  on_login(msg);
         }
         else if (msg.compare(0, _countof(ping) - 1, ping) == 0)
         {
                  on_ping();
         }
         else if (msg.compare(0, _countof(ask_clients) - 1, ask_clients) == 0)
         {
                  on_clients();
         }
    }
}

хотя конечно я немножко лукавлю, ибо написал бы с использованием memcmp что бы не создавать строку просто так:
         static const char* login = "login ";
         static const size_t login_size = _countof(login) - 1;
         if (bytes > login_size && memcmp(read_buffer_, login, login_size) == 0)
         ...
         else if (bytes == ping_size && memcmp(read_buffer_, ping, ping_size) == 0)

, и гарантированно вынес бы проверки в две инлайн функции, после чего добавил бы пару макросов ]:-> для избежания ошибок при использовании, и для компактности при написании. Собственно и on_login я бы переписал, что бы там строки не было. Зачем всё это? — для высоконагруженных приложений, которые требуют быстрого, надёжного и легко читающегося кода. Но всё же пусть мой код и быстрее, но для данного примера он избыточен, и лишь запутает читателя.


Пусть код со скобками для меня привычнее, но это нисколько не мешает воспринять условия в исходном примере кода с однострочными ифами — так очень многие пишут.

С void on_clients() скорее согласен, отступ там желателен, но почему бы не сообщить о таком допущении в личку?.. До кучи я бы, к примеру, использовал там сокращённую запись из C++11: for(const auto &b: clients), однако даже этот факт не мешает нормальному восприятию кода, к тому же это перевод, и судя по другим участкам кода — в оригинале код C++03.

c read_answer() проблем не наблюдаю, перенос длинных строк с параметрами — общепринятая практика, пусть там и не хватает отступа, и возможно этот перенос вообще не нужен, но это не мешает, и это ещё один недочёт сродни опечатки, о котором стоит сообщить в личку. Можно было даже в личку отправить предложение об использовании Astyle, но смысла обсуждать сие нет совсем никакого.

Ситуация с
typedef std::vector<client_ptr> array;
это ещё один привет из C++03.

Кстати говоря, члены класса talk_to_client:

    char read_buffer_[max_msg];
    char write_buffer_[max_msg];


должны быть объявлены вот так:

    boost::array<char, max_msg> read_buffer_;
    boost::array<char, max_msg> write_buffer_;

У меня к Вам претензия: на дворе 2013 год, а Вы почему то предлагаете boost::array а не std::array на замену! :) Ладно шучу, просто это действительно не то из-за чего могут быть проблемы с восприятием примера кода! Это пример использования, и никто не заставляет данный код копипастить и сразу же использовать в промышленном применении, это просто пример :) К слову у Александреску, в примерах, вообще местами адский-ад.

Ну и все остальное в том же духе. Конечно кому-то кажется, что человек может писать так, как ему хочется. Использовать макросы и goto, привидение типов в стиле C, free и delete вместо RAII… Надо проработать программистом не один год и в разных командах, чтобы осознать важность этих правил.

Пожалуй соглашусь только с привидением типов в стиле C при использовании в C++, а вот всё остальное: макросы, goto, и сырые указатели использовать можно если это действительно необходимо. К слову C++ из-за отсутствия make_unique даже в C++11 не полностью гарантирует безопасность использования умных указателей, а С++03 толком вообще этих возможностей не имеет (умолчим тут про auto_ptr, с которым были проблемы, о которых надо было знать, и неоднозначность с которой вообще ничего поделать было нельзя, и который ныне, к счастью — deprecated). Безусловно в Boost умные указатели появились раньше С++11, но факт остаётся фактом.

p.s: руководствуясь написанным у Вас в пункте 3 вообще не понимаю зачем было писать что либо в данном топике, если данная тема Вам не интересна.
Вообще то WinAPI, как и огромное количество другого системного кода, это C, а не C++, а стало быть наличие там данных макросов — нормальная ситуация.


Вот в том то и дело, что там код на C, поэтому макросы, хоть и вызывают неудобства, но без них не обойтись. А мы говорим о C++, и о коде, который макросов не требует вообще.

Пусть код со скобками для меня привычнее, но это нисколько не мешает воспринять условия в исходном примере кода с однострочными ифами — так очень многие пишут.


Я конечно опять нарываюсь на минусы, но все же нормальный код не должен так выглядеть. В книгах так часто пишут чтобы сократить объем. Но там и примечания ставят соответствующие. Но в статье на сайте нет смысла экономить строки.

С void on_clients() скорее согласен, отступ там желателен, но почему бы не сообщить о таком допущении в личку?


Следуя логике большинства — код компилируется? Да, значит он правильный. Так зачем что-то сообщать? Мои претензии к коду — исключительно мое личное дело, я никому не навязываю свой стиль. Тем более, что стилей очень много. Мне вот венгерская нотация не нравится, но не сообщать же теперь об этом в личку каждому автору, посмевшему ее использовать? :-)

Ситуация с
typedef std::vector<client_ptr> array;

это ещё один привет из C++03.


Я специально показал следующий пример с array из boost. Asio появился в boost начиная с версии 1.35, открываю первый попавшийся пример и вижу вот такие строки:

#include <boost/array.hpp>
...
boost::array<char, 1024> data_;


Пруф: www.boost.org/doc/libs/1_35_0/doc/html/boost_asio/example/allocation/server.cpp

Сомнительно, что автор пишет о boost asio и не знает о контейнере array. А если знает, то зачем вводить потенциально конфликтный тип данных?

Пожалуй соглашусь только с привидением типов в стиле C при использовании в C++, а вот всё остальное: макросы, goto, и сырые указатели использовать можно если это действительно необходимо.


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

p.s: руководствуясь написанным у Вас в пункте 3 вообще не понимаю зачем было писать что либо в данном топике, если данная тема Вам не интересна.


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

Ну и предлагаю завершить эту дискуссию :-)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории