среда, 2 апреля 2014 г.

PHP extension на с++

По умолчанию extension создается для компиляции с-компилятором. Для переключения на с++ есть такой вариант:
1)vim config.m4
2) в блок if test "$PHP_GT_TEST" != "no"; then
  добавить:
  PHP_REQUIRE_CXX()
  PHP_SUBST(GT_TEST_SHARED_LIBADD)
  PHP_ADD_LIBRARY(stdc++, 1, GT_TEST_SHARED_LIBADD)
3) В PHP_NEW_EXTENSION(ext, ext.c, $ext_shared)
поменять ext.c => ext.cc
4) phpize

Создать php extension в unix

Шаги:
1) cd /path_to_php_src/ext
2) ./ext_skel --extname=name
3) cd name
4) vim config.m4, там убрать dnl со строк:
dnl PHP_ARG_ENABLE(gt_test, whether to enable gt_test support,
dnl Make sure that the comment is aligned:
dnl [  --enable-gt_test           Enable gt_test support])
или со строк
dnl PHP_ARG_WITH(gt_test, for gt_test support,
dnl Make sure that the comment is aligned:
dnl [  --with-gt_test             Include gt_test support])
5) phpize
6) ./configure && make && make install

понедельник, 31 марта 2014 г.

Немного установки php

  Configuration File (php.ini) Path => /etc/php5/apache2
  Loaded configuration file => (none)

Если php в упор не видит свой ini file, хотя при компиляции ему ясно указали
  --with-config-file-path=/etc/php5/php.ini
то все просто, надо указывать папку, а не сам файл. Как-то так:
  --with-config-file-path=/etc/php5
и тогда он сам найдет там php.ini

среда, 12 февраля 2014 г.

В mysql записываются знаки вопроса вместо русского

Внезапно, встретился с такой проблемой. Из c++ передавался русский в кодировке windows-1251, таблица - cp1251_general_ci, а записываются все равно ????? Решение оказалось просто - надо после установки соединения 1 раз вызвать:
  mysql_query(conn, "SET NAMES cp1251");

вторник, 11 февраля 2014 г.

Идентификация пользователя

Стал разбираться, как бы точнее идентифицировать пользователя, нашел неплохую статью:
http://javascript.ru/unsorted/id Оставлю тут ссылку, чтобы не потерять.
Сайт, который использует это:
https://panopticlick.eff.org/index.php

понедельник, 3 февраля 2014 г.

Множественные запросы к MySQL

Множественный запрос к MySQL - это запрос, содержащий несколько выражений, записанных через ; Для того, чтобы его провести, при установке соединения нужно указать флаг CLIENT_MULTI_STATEMENTS. Как-то вот так:

mysql_real_connect(mysql, host, user, pwd, db, port, socket, CLIENT_MULTI_STATEMENTS);

Этот флаг так же ставит CLIENT_MULTI_RESULTS. Соответственно, теперь можно будет использовать. Осталось только получить результаты их выполнения. Главная - не забыть, что возвращаются результаты последовательно с каждого выражения, а не только с тех ,кто эти результаты может вернуть. Это, в общем-то, логично, дает возможность узнать, например, количество строк, затронутое каждым из выражений.

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

if (mysql_query(mysql, "CALL my_proc(@a, @b); SELECT @a, @b;") != NULL) {
  if (mysql_next_result(mysql)) > 0)
    cout << "mysql_next_result error" << endl;
    MYSQL_RES *result = mysql_store_result(mysql);
    if (result) {
      ...
      mysql_free_result(result);
    }
    else
      cout << "mysql_store_result error" << endl;
  }
else 
  cout << "mysql_query error" << endl;

Если неизвестно, возвращает ли что-нибудь выражение - можно проверить командой mysql_field_count.

Информация - из документации.

четверг, 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