Показаны сообщения с ярлыком cpp. Показать все сообщения
Показаны сообщения с ярлыком cpp. Показать все сообщения

среда, 6 мая 2015 г.

Тайна неуязвимости Человека с чемоданчиком

Раскрыт секрет неуязвимости Человека с чемоданчиком (он же G-Man) из первых версий Half-Life. Если заглянуть в исходники, становится ясно, почему в него высаживали целую обойму, а он всё равно живой:

SetImpactEnergyScale( 0.0f ); // no physics damage on the gman

четверг, 23 апреля 2015 г.

Д. Босуэлл, Т. Фаучер - Читаемый код или программирование как искусство

Небольшая книжка, в которой много полезных мелочей.

Например, более точные варианты названий для типичных функций:

send - deliver; dispatch; announce; distribute; route
find - search; extract; locate;recover
start - launch; create; begin; open
make - create; setup; build; generate; compose; new

Именованные интераторы для циклов (ui лучше, чем просто i).

Дополнительные постфиксы - size_mb, html_utf луче, чем size и html.

first/last - это включающие, а begin/end - исключающие границы.

Пометы в комментах:

TODO - задумано, но не сделано.
FIXME - известно, что есть проблема
HACK - неэлегантное решение проблемы
XXX - серьёзная проблема

Уже второе недоумение насчёт цикла do-while (которые непонятно, зачем нужен).

Разоблачён миф о том, что из функции должен быть только один выход. Он тянется из чистого C, где нередко забывали вычистить память перед выходом. Но c тех пор появились исключения, деструкторы и очистка мусора. А в C простительно писать goto cleanup;

Вообще, если удаётся уменьшить количество отступов, - это хорошо.

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

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

Переменные объявлять где используются, а не в начале блока. Объявлять всё в начале блока - дурная традиция из Pascal и раннего C (хотя в C99 уже разрешили объявлять где угодно).

Также есть приятные куски кода:

Макрос для C++, чтобы убрать warning для неопределённого конструктора копирования или оператора присваивания.

#define DISALLOW_COPY_AND_ASSIGN(ClassName) \
  ClassName(const ClassName&); \
  void operator=(const ClassName&);

И потом писать:
class ClassName {
  private:
     DISALLOW_COPY_AND_ASSIGN(ClassName);
     ...
  public:
     ...
};

понедельник, 24 ноября 2014 г.

Удивительные факты о наследовании

Говорят, с тех пор, как в Rust и Go убрали наследование, всех сторонников объектно-ориентированной парадигмы ждёт ад и погибель. Даже вроде бы адекватные блоггеры пишут об этом и советуют забыть про ООП. Код, который приводится в качестве примера, настолько прекрасен, что я не могу его не привести:

trait Animal {
  def word: String
  def talk() { println(this.word) }
}
class Cat extends Animal { def word = "Meow!" }

class Dog extends Animal { def word = "Woof!" }

class JustACat { def meow = "Meow!" }

class HappyCat extends JustACat with Animal {
  def word = meow + " :)"
}

class SadCat extends JustACat with Animal {
  def word = meow + " :("
}
val dog = new Dog()
dog.talk

"Вот по большому счету и все, что нужно знать об АТД и классах типов" сообщает нам автор. "Забудьте об ООП! Используйте АТД и тайпклассы!".

Не имею ничего против trait-ов и языка Scala, но такой пример реализации ООП порадует разве что борцов за права домашних животных. Абсолютно непонятно, какое отношение к реальным программам имеют все эти котики и собачки и как собрать из этих грузок и перегрузок что-то работающее. Вспоминать Майерса или Буча тут не стоит - куда уместней общеизвестная песенка:

Я жму на велике любимом,
Он зовется "Украина"
Но седло от унитаза,
А педаль от пианино.
Все потырили друзья,
В двух местах сломали раму,
Ну и пусть себе смеются,
Металлисты-наркоманы!

Раз уж пошла такая путаница, надо разобраться, какое бывает ООП. Обычно под ним понимают что-то в духе C++/C#/Java - модификаторы public/private/protected, виртуальные функции, наследование, и проч., проч., проч.

Но есть языки наподобие Python, Ruby или PHP - где приватных полей нет вообще, а приватность функций зависит от доброй воли пользователя библиотеки, а типы можно сравнивать через duck typing. При этом наследование в них очень разное: в PHP оно аналогично Java (можно наследовать только от одного класса), Python разрешает множественное наследование, а в Ruby разрешает новомодные подмешивания (впрочем, большинство пользователей про них не в курсе).

Есть, наконец, JavaScript, где наследование через прототип настолько загадочно, что некоторые даже пишут свои обёртки, чтобы заменить его на что-то более привычное. И многие из этих обёрток реализуют и множественное тоже.

Ну и на закуску - есть известные реализации псевдо-ООП на C (вроде тех, что встроены в GTK и WinAPI), есть языки-надстройки вроде Lua, и громадную, ещё с начала 1990-х историю ORM для записи унаследованных объектов в реляционные базы данных.

Как легко заметить, главное в ООП - наследование, а наличие приватных полей и виртуальных функций - дело десятая. Куча народу пишет на JavaScript и не в курсе про его систему наследования, или на Python, а наследуют "как привыкли", с интерфейсами и основным классом.

О чём и было сказано уже в другом блоге, где дискуссия продолжалась: Ситуация усугубляется еще и тем, что из широкоизвестных ОО языков чисто ОО языками являются всего два -- Java и Eiffel.

четверг, 7 марта 2013 г.

QT: error: cannot find -lQtMultimedia

The QtMultimedia library is not available in Ubuntu. If you’re trying to create in Qt a project that uses it, it will fail with following error:

error: cannot find -lQtMultimedia

To fix it, you have to install qtmobility and use multimedia items from this lib. Install qtmobility-dev and libdeclarative-multimedia. Then remove QT += multimedia from .pro file and add INCLUDEPATH to QtMobility and QtMultimediaKit directories.

воскресенье, 24 февраля 2013 г.

“Already defined” linker error

This error is very common, when you try to compile in Visual Studio a huge cross-platform C++ project.

This is because of standart C++ templates, that are released in Visual Studio libs and in additional UNIX libs too. So, you have to exclude one of them to use another.

Just add one of them, go to Project Properties / Linker / Input / Ignore Specific Default Libraries and add the standard lib that produces conflict. For me it was enough to add LIBCMT.LIB.

LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt

LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or corrupt

It's a common error when you compile C++-projects with Visual Studio 2010. Ift happens when 2 version of Visual Studio are installed on same PC (like 2010 and 2012) and cause conflict between two versions of cvtres.exe.

To fix it, go to the bin directory of Visual Studio 2010 (c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\ or something like this) and rename cvtres.exe to cvtres0.exe.

Don't worry, it willn’t break linker of 2010,

пятница, 8 июня 2012 г.

C++: Счастливый билет с длинной арифметикой

Счастливый трамвайный билет - это такой, у которого сумма первых трёх цифр номера равна сумме трёх последних. При встрече его полагается съесть.

Сколько таких билетов всего? Какова вероятность встретить счастливый?

Алгоритм для этих вычислений в Википедии настолько ужасен, что народ с Хабра довольно быстро породил в комментариях огромное количество вариантов на C++, Perl и... SQL.

Я тоже не смог пройти мимо и написал считалку на C++ с арифметикой указателей и поддержкой длинной арифметики. При желании она может считать хоть 12-значные числа.

Оптимизировал всем, о чём пишут в пособиях для начинающих. Получилась миниатюрная машина Тьюринга, которая бегает указателем по числам и считает разряды:

void inline count_lucky(unsigned int num_length_val)
{
  bool is_odd = (num_length_val % 2) > 0;
  unsigned int num_length = (is_odd) ? num_length_val - 1 : num_length_val;
  register unsigned int lucky=1, total=pow(10.0,(int)num_length), i;
  char *num = new char[num_length], *end=num+(num_length-1);
  register char *pos=end;
  for(i=0; i < num_length; i++)
    num[i]=0;
  while(pos>=num)
  {
     if((*pos)<9)
     {
        (*pos)++;
        if(pos==end)
        {
           if(is_lucky((unsigned char*)num, num_length))
             lucky++;
        }
        else
          pos++;
     }
     else
     {
       *pos=-1;
       pos--;
     }
  }
  printf("Lucky are %d of %d (%.3f%%)\n",(is_odd) ? lucky * 10 : lucky, (is_odd) ? total * 10 : total,((float)lucky/total*100));
  delete[] num;
}
bool inline is_lucky(unsigned char* items, unsigned int items_count)
{
  unsigned char val1=0, val2=0;
  unsigned int half = items_count >> 1;
  for(unsigned int i=0; i < half; i++)
  {
    val1 += items[i];
    val2 += items[items_count-1-i];
  }
  return (val1==val2);
}
Увы! Как показывает практика, уже на 7-значном билете тупой перебор, развёрнутый и оптимизированный компилятором, оказывается эффективней. Похоже, компилятор попросту выполняет сам большую часть явно заданных вычислений.
В этом легко убедиться, запустив тест хотя бы для 7 разрядов:

void inline count_lucky(unsigned int num_length);
bool inline is_lucky(unsigned char* items, unsigned int items_count);

int _tmain(int argc, _TCHAR* argv[])
{
  clock_t start = clock(), mine;
  count_lucky(7);
  mine=clock();
  printf("Mine time was: %d\n", (mine - start));
  count_lucky_fromwiki();
  printf("His time was: %d\n", (clock() - mine));
  return 0;
}

void count_lucky_fromwiki()
{
  int total=0,lucky=0;
  for(int i1=0; i1<10; i1++)
    for(int i2=0; i2<10; i2++)
      for(int i3=0; i3<10; i3++)
        for(int i4=0; i4<10; i4++)
          for(int i5=0; i5<10; i5++)
            for(int i6=0; i6<10; i6++)
              for(int i7=0; i7<10; i7++, total++)
                  if(i1+i2+i3 == i5+i6+i7)
                    lucky++;
  printf("Lucky are %d of %d (%.3f%%)\n",lucky,total,((float)lucky/total*100));
}

Человек уже не может обогнать машину :(. Слава роботам!

В качестве утешения - на 8 вложенный циклах валился компилятор из Borland C++ Builder 6.

C#: Difference between && and &

(I'll write in English, because this problem is widely asked.)

What's the difference between && and & or || and | in C#? In C++ it's the difference between logical and bitwise AND.

The most of its usage as connected with universality of if in C++. For example, if(a) in C++ never executes when:
  • a is false (because false = 0)
  • a is null (because null = 0)
  • a is a number <=0.

So, if(a == true) or  (a > 0) is useless in C++. if(a) is much shorter.


JavaScript implemented 1 more option:
  • a is an empty string ("")
C# is a strongly-typed langauge and the if in C# works different. It accepts only boolean values in scope.

So, the only difference between | an || and & and && in C# is the short-circuiting.

i.e.:


if(foo() && boo())


if foo() == false, boo() willn't be executed.


if(foo() & boo())


The foo() and boo() will be executed.


So, never write this way:


if(a != 0 & b / a > 100)


if a == 0, it will fall in Division by DivideByZeroException. The right variant is:


if(a != 0 && b / a > 100)

The same thing:

 if(item != null & item.isActive())

Never write this way. It has to be written like this:


 if(item != null && item.isActive())

пятница, 27 апреля 2012 г.

C++: Вопросы на собеседовании

"Вопросы на собеседовании по С++" - один из самых популярных запросов, по которым находят этот блог.

А значит, настало время расширить этот постинг.

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

Итак, понеслось:
Напоследок - картинка от jia3ep-а.

четверг, 13 октября 2011 г.

C++: Размер массива, switch в одну строку и cдвиги

Размер массива в C/C++:

MyHugeStructure array[100];
int array_size = sizeof(array)/sizeof(*array);

for(int i = 0; i < array_size; i++) 
    array[i].id = i;

Следующие два варианта актуальны и для других языков.

Switch в одну строку с поддержкой всех типов:
str = number == 1 ? "one" : 
      number == 2 ? "two" :
      number == 3 ? "three" :
      number == 4 || rand() == 42 ? "four" :
      number == 5 ? "five" :
      "unknown number";

Стандарт C - штука нестрогая. Например, char - это не 1 байт, не 2 и не 4. char - это sizeof(char) и единственное ограничение: sizeof(char) < sizeof(int). Вспоминаем, что из себя представляют числа в памяти и поэтому НИКОГДА не пытаемся поделить на 2 через сдвиг отрицательное число.
//ТАК ДЕЛАТЬ НЕЛЬЗЯ!!
int a = -2;
printf("%dn", a >> 1);

Взято отсюда