понедельник, 12 декабря 2011 г.

язык С : true и false

Обычно true и false в С определяют так:


#define TRUE 1
#define FALSE 0
Но недавно встретил интересную запись следующего вида:

#define TRUE (0 == 0)
#define FALSE (0 != 0)

Настойка IntelliJ IDEA для clojure.

Меня заинтересовал язык clojure, поэтому решил попробовать новую для себя ide IntelliJ IDEA. Версию community edition можно скачать на официальном сайте. Для сборки проектов я использую leiningen . В этом топике я покажу какие необходимо сделать настройки.

Для начала необходимо установить два плагина: La Clojure и Leiningen с помощью Plugin Manager.


Далее нужно настроить плагин Leiningen. Для этого открываем настройки File->Settings(Ctrl+Alt+S), находим строку с данным плагином и устанавливаем путь до утилиты lein.


После чего создадим проект с помощью lein из консоли:


lein new example
Откроем созданный проект в ide, используя Open Project, и выберем файл project.clj. Далее IntelliJ IDEA сгенерит все необходимые для нее файлы.

Следующий шаг это добавление необходимых зависимостей для проекта. На данном примере я добавлю clojure-contrib и sqlitejdbc. Файл project.clj примет вид:


(defproject sqlite-example "1.0.0-SNAPSHOT"
  :description "sqlite example"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [org.clojure/clojure-contrib "1.2.0"]
                 [sqlitejdbc "0.5.6"]])
После чего нажимаем на вкладку Leiningen, выбираем deps и запускаем загрузку необходимых библиотек.

Осталось лишь добавить установленные библиотеки в проект idea. Это необходимо сделать, потому что мы не создавали новый проект, а открыли уже существующий, сгенерированный с помощью утилиты lein. Выбираем File->Project Structure (или Ctrl+Alt+Shift+S), далее Modules и выбираем закладку Dependencies, затем нажимаем кнопку Add -> Libraries.

Нажимаем Attach Jar Directories и выбираем директорию с установленными библиотеками:

Теперь запускаем REPL - Tools->Start Clojure Console(Ctrl+Shift+F10). На этом настройка ide закончена.

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

Паттерны для создания рекурсивных функций.

Читая An Introduction to Programming in Emacs Lisp, наткнулся на описание простых рекурсивных паттернов и решил сделать их описание.

Паттерн every.

В данном паттерне действие выполняется над каждым элементом списка. Алгоритм паттерна следующий:


  • Если список пустой, то возвращаем nil.

  • В противном случае работаем над началом списка(операция car). После чего делаем рекурсивный вызов с использованием cdr или комбинируем результат функции с помощью cons.

Пример использования:

(defun square-each (numbers-list)
"Square each of a NUMBERS LIST, recursively."
    (if (not numbers-list)
        nil
        (cons (* (car numbers-list) (car numbers-list)) 
            (square-each (cdr numbers-list)))))

(square-each ’(1 2 3))
⇒ (1 4 9)
В данном примере каждый элемент списка возводится в квадрат.
Еще один пример:

(defun print-elements-recursively (list)
"Print each element of LIST on a line of its own. Uses recursion."
    (when list
        (print (car list))
        (print-elements-recursively (cdr list))))
В приведенном примере функция рекурсивно выводит каждый элемент.

Паттерн accumulate.

В этом паттерне действие производится над каждым элементом списка и результат
этого действия аккамулируется с результатом последующих элементов. Алгоритм имеет вид:


  • Если список пуст, то возвращается нуль или любая другая константа.

  • Иначе берется первый элемент списка и объеденяется с последующими элементами
    рекурсивного вызова с помощью функции + или подобными ей комбинирующими функциями.
Пример:

(defun add-elements (numbers-list)
"Add the elements of NUMBERS-LIST together."
      (if (not numbers-list)
         0
         (+ (car numbers-list) (add-elements (cdr numbers-list)))))

(add-elements ’(1 2 3 4))
⇒ 10
Таким образом функция выводит сумму всех элементов списка.

Паттерн keep.

В данном паттерне каждый элемент списка проходит проверку. Если он удовлетворяет условию, то результат действия сохраняется.
Алгоримт имеет вид:

  • Если список пуст, то возвращаем nil.
  • Если начальный элемент списка проходит проверку, то комбинируем его с помощью cons и делаем рекурсивный вызов с хвостом списка(cdr).
  • В противном случае если первый элемент не удовлетворяет условию, то действие над данным элементом пропускается и с помощью рекурсивного вызова переходим на следующий элемент.
Пример:

(defun keep-three-letter-words (word-list)
"Keep three letter words in WORD-LIST."
     (cond ((not word-list) nil)
           ((eq 3 (length (symbol-name (car word-list))))
               (cons (car word-list) (keep-three-letter-words (cdr word-list))))
           (t (keep-three-letter-words (cdr word-list)))))

(keep-three-letter-words ’(one two three four five six))
⇒ (one two six)
В выше приведенном примере выделяется список символов, длина которого равна 3.

четверг, 8 сентября 2011 г.

Создание документации и доступ к ней - emacs-docs.

Возникла необходимость в работе с документацией по исходному коду. Конечно есть много готовых инструментов, но хотелось бы что-нибудь интегрированное к emacs. Поэтому решил сделать модуль для него на основе org-mode. В начале упор будет сделан на язык С. Исходный код проекта находится на github.

вторник, 6 сентября 2011 г.

Перевел cl-event-callback на bordeaux-threads.

Заменил sb-thread на bordeaux-threads. Алгоритмы работы модуля остались без изменений. Теперь необходимо добавить потоко-безопасность.

git. Создание (удаление) удаленной ветки.

Постоянно забываю команды git для создания новой удаленной ветки. Решил отметить это тут:


git push origin origin:refs/heads/new_branch
git checkout --track -b new_branch origin/new_branch
Чтобы удалить удаленную ветку:

git push origin :heads/new_branch
Вывести список удаленных веток можно командой:

git branch -r

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

Использование netlink для определения изменения маршрутов ipv6 в Linux.

   Протокол ipv6 позволяет конфигурировать сеть без учатия пользователя с помощью icmpv6, если узел работает в качестве клиента(forwarding установлен в 0).
Поэтому возникает задача: как отследить эти изменения. Как оказывается, это достаточно просто, если использовать протокол netlink. Описание данного протокола можно найти здесь (eng) и здесь(рус.).
   Для вывода информации об измененных маршрутах я создал функцию show_route. Вот ее исходный код:

/*
buffer - хранит полученные сообщения от netlink
size - размер полученных данных
*/
void show_route(char *buffer, int size)
{
   /*
   nlmp - указатель на структуру заголовка netlink
   routemsg - полезная нагрузка
   tb[RTA_MAX+1] - атрибуты маршрута
   */
   struct nlmsghdr *nlmp;
   struct rtmsg       *routemsg;
   struct rtattr * tb[RTA_MAX+1];

   /*
   Данные в буфере имеют следующий вид:
   |nlmsghdr|data|nlmsghdr|data|....|nlmsghdr|data|
   |-------------------size-----------------------|
   */

   for(nlmp = (struct nlmsghdr *)buffer; size > sizeof(*nlmp);){  

      int len;
      int req_len;
      /*Проверяем целостность netlink сообщения*/
      if (!NLMSG_OK(nlmp, size)) {
         perror("NLMSG not OK\n");
         return;
      }
      /*Это размер сообщения netlink*/
      int len = nlmp->nlmsg_len;
      /*Получаем размер полезной нагрузки*/
      int req_len = len - sizeof(*nlmp);
      /*Проверяем тип сообщения*/
      /*Если он не относится к маршрутам, то переходим на следующее сообщение*/
      switch(nlmp->nlmsg_type){
      case RTM_DELROUTE:
         printf("A route was deleted\n");
         break;
      case RTM_NEWROUTE:
         printf("A route was added\n");
         break;
      default:
         size -= NLMSG_ALIGN(len);
         nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
         continue;
     }
     /*Получаем указатель на полезную нагрузку и , после чего, выделяем атрибуты маршрута*/
     routemsg = (struct rtmsg *)NLMSG_DATA(nlmp);
     /*Данная функция описана ниже.*/
     parseRtattr(tb, RTA_MAX, RTM_RTA(routemsg), len);

     /*Код ниже выводит атрибуты маршрута*/
     if(tb[RTA_IIF]){
        int iface;
        iface = *(int*)RTA_DATA(tb[RTA_IIF]);
        printf("An input iface is %d\n", iface);

     }
     if(tb[RTA_OIF]){
        int iface;
        iface = *(int*)RTA_DATA(tb[RTA_OIF]);
        printf("An output iface is %d\n", iface);
     }
     if(tb[RTA_DST]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_DST]), routeAddr, sizeof(routeAddr));
        printf("Destination route is %s\n", routeAddr);
     }else if (routemsg->rtm_dst_len) {
        printf( "0/%d \n", routemsg->rtm_dst_len);
     } else {
        printf("Destination route is default\n");
     }

     if(tb[RTA_SRC]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_SRC]), routeAddr, sizeof(routeAddr));
        printf("Source route is %s\n", routeAddr);
     }
     if(tb[RTA_GATEWAY]){
        char routeAddr[128];
        inet_ntop(AF_INET6, RTA_DATA(tb[RTA_GATEWAY]), routeAddr, sizeof(routeAddr));
        printf("Gateway is %s\n", routeAddr);
     }
    if(tb[RTA_PRIORITY]){
        int metric ;
        metric = *(int*)RTA_DATA(tb[RTA_PRIORITY]);
        printf("Mertic is %d\n", metric);
     }
     /*Переходим на следующее сообщение*/
     size -= NLMSG_ALIGN(len);
     nlmp = (struct nlmsghdr*)((char*)nlmp + NLMSG_ALIGN(len));
  }//for(nlmp = (struct nlmsghdr *)buffer; size > sizeof(*nlmp);){
}
Полезная нагрузка в данном случае состоит из заголовка и атрибутов
маршрута. Поэтому общий вид пакета имеет следующий вид:


Таким образом, функция для выделения атрибутов:

void parseRtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
       memset(tb, 0, sizeof(struct rtattr *) * (max + 1));

       while(RTA_OK(rta, len)){
             if(rta->rta_type <= max){
                   tb[rta->rta_type] = rta;
             }

             rta = RTA_NEXT(rta, len);
      }
}

Теперь можно написать main функцию:

int main(int argc, char *argv[])
{
    int nd = 0;
    struct sockaddr_nl *local;
    char buffer[16384];

    nd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

    local = malloc(sizeof(struct sockaddr_nl ));
    memset(local, 0, sizeof(struct sockaddr_nl));

    local->nl_family = AF_NETLINK;
    /*Принимаем изменения, связанные с маршрутами ipv6*/
    local->nl_groups = RTMGRP_IPV6_ROUTE;
    local->nl_pid = getpid();

    if(bind(nd, (struct sockaddr *)local , sizeof(struct sockaddr_nl)) < 0){
         printf("Error bind\n");
         return 1;
    }

    while(1){
       /*
        Эта структура непосредственно передается через сокет.
        Она содержит в себе  указатель на блок полезных данных,количество
        данных блоков, а так же ряд дополнительных флагов и полей
      */
       struct msghdr msg;
       /*
        Эта структура служит хранилищем полезных данных,
        передаваемых через сокеты netlink. Полю iov_base присваивается указатель
        на байтовый массив. Именно в этот байтовый массив будут записаны
        данные сообщения.                   
       */
       struct iovec iov;
       int size = 0;

       iov.iov_base = buffer;
       iov.iov_len = 16000;

       msg.msg_name = &local;
       msg.msg_namelen = sizeof(*local);
       msg.msg_iov = &iov;
       msg.msg_iovlen = 1;

       size = recvmsg(nd, &msg, MSG_DONTWAIT);
       if(size  < 0){
             if(errno == EINTR || errno == EAGAIN){
                   usleep(250000);
                   continue;
            }
      }

      show_route(buffer, size);
   }
} 

среда, 24 августа 2011 г.

Пакет cl-event-callback

       Оформил код в виде пакета. Но пока есть минус - так как использую потоки sbcl, то он работает только под ним, поэтому собираюсь перевести с sbcl-threads на bordeaux-threads.  Ссылка на проект: github.

Пакет обработки событий на lisp

     Мне понадобился модуль, поддерживающий обработку событий, но я ничего не нашел(возможно плохо искал). Поэтому решил написать пакет, решающий эту задачу, исходники которого находятся на github
     Итак, реализация  похожа на сигнал-слотовую модель(например, как в Qt), то есть при определенном событии происходит подписанное на него действие. Пока реализовано:
  1. Три состояние события - событие произошло(:trig),  сброс в исходное состояние (:not-trig) и выполнение подписанного  на событие действия один раз (:trig-stop)  
  2. Добавление/удаление именованного события и его обработчика
  3. Запуск и останов событийной модели
  4. Автоматическое и ручное управление состояниями события

Пример использования:
1. Автоматическое управление


(in-package #:cl-event-callback)

;Объявим переменную, при изменении которой будет присходить событие
(defparameter *a* 1)

; Данная функция будет выполняться при получении события
(defun test () (print "test"))

;Создаем именнованное событие. В данном случае, если *a* больше 3, то 
;будет вызываться функция test.
(defun new-event ()
       (connect '(test) 'test-event '(> *a* 3)))

;Функция для изменения состояния переменной *a*
(defun event-change (var)
       (setq *a* var))

;Удаляем именнованое событие
(defun del-test-event ()
       (disconnect 'test-event))

(defun main ()
       (new-event)		; <- Создаем событие
       (thread-run-callbacks)   ; <- Отслеживаем изменения состояния
       (event-change 5)         ; <- Изменяем переменную *a*, чем вызываем событие
       (sleep 3)
       (event-change 1)         ; <- Возвращаем состояние *a*
       (del-test-event)         ; <- Удаляем событие
       (thread-stop-callbacks))

2. Ручное управление


(in-package #:cl-event-callback)

;Объявим функции для тестов
(defun test1 () (print "test1"))

(defun test2 () (print "test2"))

;Создадим события
(defun create-events()
       (connect '(test1) 'test-one)
       (connect '(test2) 'test-two))

;Функция удаления событий
(defun delete-events ()
       (disconnect 'test-one)
       (disconnect 'test-two))


(defun main ()
       (create-events)			     ; <- Создаем события
       (thread-run-callbacks)                ; <- Отслеживаем изменения сотояния событий
       (set-event-state 'test-one :trig)     ; <- Вызываем событие вручную
       (set-event-state 'test-two :trig-stop); <- Функция, подписанная на это событие вызовется один раз, после чего состояние события переведется в :not-trig
       (sleep 2)
       (set-event-state 'test-one :not-trig) ; <- Это событие переводим в состояние :not-trig
       (delete-events)
       (thread-stop-callbacks))
На текущий момент необходимо доделать следующее:
  • Оформить код в виде пакета
  • Добавить потоко-безопасность

четверг, 30 июня 2011 г.

Простая установка пакетов при помощи asdf.


     Одним из вариантов установки пакетов в sbcl является asdf. Если пакет можно установить из репозитория, то можно воспользоваться пакетом  asdf-install. Для начала нужно подгрузить данный модуль:
                                                                   
(require 'asdf-install)   
или
                                                                                                                          
(require "asdf-install")
После чего выполняем следующую команду:
                                                                       
(asdf-install:install :package-name)
где package-name - это название устанавливаемого пакета.
     Можно так же устанавливать пакет из исходных кодов. В этом  случае делаются следующие шаги:
1. По умолчанию пакеты ставятся в ~/.sbcl, поэтому делаем
символьную ссылку в директории ~/.sbcl/system на package_name.asdf
2. Если скаченный пакет не находится в директории ~/.sbcl/site,
то необходимо добавить путь в asdf командой
                                                                                 
(push (truename #P"/path/to/packet") asdf:*central-registry*) 
3. После чего устанавливаем пакет командой:
                                                                                   
(asdf:oos 'asdf:load-op :package_name)