четверг, 30 января 2014 г.

typedef в классах

typedef можно использовать внутри классов. Это удобно, например, когда нужен какой-нибудь специфический map внутри этого класса и потом итераторы к нему где-то во вне.
Используется это(опять же, например, так):

class MyClass {
public:
  typedef map<int, std::string> mymap_type;
private:
  mymap_type mymap;
public:
  ...
  mymap::iterator get_map_beg() {
    return mymap.begin();
  }
}

...

int main() {
  MyClass myclass_var;
  MyClass::mymap_type::iterator it = myclass_var.get_beg();
}

Очевидно, сам класс итератора можно было тоже сделать typedef в классе =)

Плюсов довольно много. Значимые для меня:
 + Уходят длинные некрасивые строки с шаблонами, читать становится проще
 + Проще потом поменять тип, если что-то изменилось. Не надо лазить по всему коду и править типы.

Минусы:
 - могут возникнуть проблемы с зависимостями

среда, 29 января 2014 г.

Немного о блокировках в хранимых процедурах MYSQL

Возникла необходимость вызывать одну и туже хранимую процедуру MYSQL из разных процессов. И невольно задумался о синхронизации средствами самой СУБД. Вот, что я раскопал:

Стандартные LOCK_TABLE() внутри хранимых процедур вызывать нельзя.

Транзакции (START TRANSACTION) сами блокировки не ставят, так что просто создать innodb таблицу не поможет =)

Есть такой вариант : SELECT ... FOR UPDATE. Он блокирует только строки, которые прочитал до дальнейшего UPDATE. Этот UPDATE должен быть в пределах этой же транзакции, иначе будут проблемы (блокировка не снимется, насколько я понял)

Подошедший мне вариант был такой:  GET_LOCK(name, timeout) и RELEASE_LOCK(name).
Работает это все по принципу мутексов. Успешная блокировка возвращает 1, т.е.
SELECT SET_LOCK("my_lock", 10);
 -> 1
Если вернулся 0 - значит заблокировал кто-то другой и таймаут истек. А если вообще NULL - то произошла ошибка.
name - задается строкой.
SELECT RELEASE_LOCK("my_lock") вернет 1, если успешно снял блокировку, 0 - если не смог, потому что ее выставил другой поток и NULL - если такой блокировки нет (например, уже сняли, или таймаут истек.)

Для работы с RELEASE_LOCK неплохо подходит оператор DO
DO RELEASE_LOCK("my_lock");
В отличие от SELECT он просто не вернет результат который очень часто от RELEASE и не требуется.

Проверить наличие блокировок можно функциями IS_FREE_LOCK(nameи IS_USED_LOCK(name).
Принцип возврата - такой же. 1 - если ответ "да", 0 - если "нет" и NULL - если такой блокировки вообще не найдено.

Источник информации - документация :)

MYSQL таблица MEMORY ограничена в размере и не заполняется

При тестах понадобилось создать таблицу типа MEMORY и оказалось, что необходимый объем данных не влезает. Выше определенного числа строки просто не создаются с ошибкой:
The table НАЗВАНИЕ ТАБЛИЦЫ is full

По факту - у MYSQL просто кончилась память под размещение данных в куче. Легко изменить это в настройках MYSQL (в Linux - по умолчанию /etc/my.cnf):
max_heap_table_size=256M

среда, 22 января 2014 г.

Локальные данные в потоках

Появилась необходимость хранить в каждом потоке свои static локальные данные.
Решение:

В c++11 появился thread_local, который объявляет переменную локальной для потока. Соответственно, сколько потоков - столько переменных будет создано.
В gcc для этой же цели есть __thread. Используется оно, как и другие спецификаторы класса:
__thread int i;
Можно использовать совместно с static или extern, но тогда оно обязательно должно стоять сразу после них.
Ссылка на официальный сайт.

И еще немного с++

Встретил очередной нюанс с++:

Если класс не содержит явного копирующего конструктора или operator=, то они создаются неявно.
Об этом надо помнить.

Допустим у нас есть класс, который содержит в себе указатель на свой буффер.



class buf_t
{
  unsigned char* data;
public:
  buf_t(size_t buf_size)
  {
    data=new unsigned char[buf_size];
    printf("buf_t(): data=%p\n",data);
  }

  ~buf_t()
  {
    printf("~buf_t(): data=%p\n",data);
    delete[] data;
  }

};


Легко ошибиться и выполнить присваивание или вызвать копирующий конструктор для такого объекта.
Вот такой код запросто скомпилируется и будет работать неправильно.


int main(int argc,char** argv)
{
  buf_t a(1000);

  buf_t b(a);

  buf_t c(500);
  c=a;


  return 0;
}


Вывод программы будет такой:


buf_t(): data=00902DCC
buf_t(): data=009031B8
~buf_t(): data=00902DCC
~buf_t(): data=00902DCC
~buf_t(): data=00902DCC



Т.е. мы присвоили указатель data объекта a объектам b и c.
В результате у нас 3 раза освободился один и тот же указатель, а data объекта c вообще потерялся.

Решение — сделать private копирующий конструктор и operator=. При этом достаточно их просто объявить, тело функций добавлять не надо.


class buf_t
{
  unsigned char* data;
public:
  buf_t(size_t buf_size)
  {
    data=new unsigned char[buf_size];
    printf("buf_t(): data=%p\n",data);
  }

  ~buf_t()
  {
    printf("~buf_t(): data=%p\n",data);
    delete[] data;
  }

private:
    buf_t(const buf_t&);
    void operator=(const buf_t&);

};

Описание взял отсюда, там же есть еще интересные факты:
http://www.rsdn.ru/forum/cpp/4896711.all
Стал работать с gdlib.
Нашел актуальное описание API тут:
http://www.boutell.com/gd/manual2.0.33.html#reference