понедельник, 12 сентября 2016 г.

[prog.flame] Теплая ламповая сишечка, говорили они...

Довелось заглянуть в исходники одной сишной библиотеки. Что за библиотека и зачем я туда полез -- дело десятое, к теме сегодняшнего разговора отношения не имеющее. А вот то, какой код обнаруживается в нее в потрохах, заслуживает пристального внимания. Ибо код этот является отличным образчиком тщательно написанного обычного кода на C. Подчеркну -- обычного кода. Написанного обычными разработчиками, а не гуру калибра Линуса Торвальдса. Посему выглядит обычный код на C вот так:

int 
ub_ctx_add_ta(struct ub_ctx* ctx, const char* ta)
{
   char* dup = strdup(ta);
   if(!dup) return UB_NOMEM;
   lock_basic_lock(&ctx->cfglock);
   if(ctx->finalized) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_AFTERFINAL;
   }
   if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, dup)) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_NOMEM;
   }
   lock_basic_unlock(&ctx->cfglock);
   return UB_NOERROR;
}

Классно, не правда ли? Прилежанию автора можно только позавидовать.

А потом настучать по рукам. За то, что не использует хотя бы идиому goto cleanup. Если уж перейти на C++ ума не хватило.

Если вам кажется, что это что-то из ряда вон, то это вы еще не видели следующей функции, которая в коде идет прямо вслед за только что показанной:

int 
ub_ctx_add_ta_file(struct ub_ctx* ctx, const char* fname)
{
   char* dup = strdup(fname);
   if(!dup) return UB_NOMEM;
   lock_basic_lock(&ctx->cfglock);
   if(ctx->finalized) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_AFTERFINAL;
   }
   if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_file_list, dup)) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_NOMEM;
   }
   lock_basic_unlock(&ctx->cfglock);
   return UB_NOERROR;
}

Ну как? Ничего не напоминает? Ну тогда давайте посмотрим еще одну, которая идет прям следом:

int
ub_ctx_add_ta_autr(struct ub_ctx* ctx, const char* fname)
{
   char* dup = strdup(fname);
   if(!dup) return UB_NOMEM;
   lock_basic_lock(&ctx->cfglock);
   if(ctx->finalized) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_AFTERFINAL;
   }
   if(!cfg_strlist_insert(&ctx->env->cfg->auto_trust_anchor_file_list, dup)) {
      lock_basic_unlock(&ctx->cfglock);
      free(dup);
      return UB_NOMEM;
   }
   lock_basic_unlock(&ctx->cfglock);
   return UB_NOERROR;
}

А следом идет еще одна такая же. С точностью до одного имени при вызове cfg_strlist_insert во втором if-е.

Лепота!

Боюсь, если теперь мне кто-нибудь всерьез начнет доказывать, что писать код на C проще, что код на C понятнее, что бинарники не пухнут за счет шаблонов, что нет непоняток с тем, какое, где и откуда может выскочить исключение... Так вот, боюсь, я не только не смогу всерьез относиться к таким словам. Мне еще будет настолько жаль потраченного на общение с такими гореспециалистами времени, что от досады могу не сдержаться.

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

void 
ub_ctx_add_ta(ub_ctx & ctx, std::string ta)
{
   std::lock_guard<some_lock> lock(ctx.cfglock);
   if(ctx.finalized)
      throw some_exception(UB_AFTERFINAL);
   ctx.env->cfg->trust_anchor_list.insert(std::move(ta));
}

И это все!

Ну ладно, исключений еще некоторые боятся как огня, поэтому строчат простыни кода с ручной очисткой ресурсов и if-ом на каждый чих, проявляя чудеса внимания и усидчивости. Давайте представим, как этот же код можно записать на C++ без исключений.

int 
ub_ctx_add_ta(ub_ctx& ctx, const char* ta)
{
   std::unique_ptr<chardecltype(&free)> dup(strdup(ta), &free);
   if(!dup) return UB_NOMEM;
   std::lock_guard<some_lock> lock(ctx.cfglock);
   if(ctx.finalized)
      return UB_AFTERFINAL;
   if(!cfg_strlist_insert(&ctx->env->cfg->trust_anchor_list, std::move(dup)))
      return UB_NOMEM;
   return UB_NOERROR;
}

Неужели это, блин, настолько сложнее, чем сплошная копипаста на сишечке???

Даже над уже показанным кодом можно и еще постебаться. А ведь это были самые простые и компактные примерчики. Уверяю, там есть и еще похлеще.

В общем, чего хочу сказать. На дворе, мать его, XXI век. Для дебилов, которые не сумели освоить C++, но вынуждены писать нативный код, уже есть Go. Тупой, безопасный и со специальной конструкцией defer, специально вставленный в этот убогий язык, дабы умственно отсталым не приходилось прибегать ни к копипасте, ни к goto cleanup для очистки ресурсов. Еще есть D со scope(exit). А еще год назад релиз Rust-а состоялся. Но нет. Дебилы продолжают педалить мегатонны говнокода на теплой ламповой сишечке. И еще радостно визжать о том, насколько им нравится это делать.

Комментариев нет: