libsh - это библиотека на C и для C. Её можно использовать и в C++, но см. далее. Её, в принципе, можно использовать в любом языке, если написать биндинги.
libsh должна работать в любой операционной системе, в том числе в Windows и в UNIX-подобных системах (Mac OS X, GNU/Linux). Тем не менее, в первую очередь либа предназначена для UNIX-подобных систем.
Лицензия: GPLv3+
libsh - это:
- Исключения для C. Исключения реализованы с использованием только стандартных средств C, т. е. libsh в прямом смысле добавляет исключения в C, и это будет работать в любом компиляторе и ОС (реализовано с помощью setjmp/longjmp/sigsetjmp/siglongjmp)
- Обёртки вокруг стандартных функций C, например, fopen, и стандартных функций POSIX (только при их наличии), например, open, которые бросают вышеупомянутые исключения в случае ошибок
- Дополнительные функции, реализовывающие часто нужные операции на UNIX-подобных системах (если у вас такая система), например, копирование данных из одного файлового дескриптора в другой. Эти функции реализованы с помощью вышеупомянутых обёрток, поэтому они тоже могут бросать исключения
Вам нужен cmake версии как минимум 3.1.0, make и компилятор C. WARNING! cmake 3.1.0 только что вышел, скорее всего его нет в репозитории вашей ОС. Итак, идём в https://github.com/safinaskar/libsh/releases , скачиваем последний libsh-extended-$VERSION.tar.gz (разумеется, под $VERSION я подразумеваю конкретный номер версии) и:
$ tar -xf libsh-extended-$VERSION.tar.gz
$ mkdir libsh-build
$ cd libsh-build
$ cmake -DCMAKE_INSTALL_PREFIX=/путь/куда/ставить ../libsh-$VERSION
$ make
$ make install # Если /путь/куда/ставить требует прав рута, то эту команду нужно запускать от рута
Если очень хочется собрать и установить именно из git'а, что ж, можно и из git'а, только меньше шансов, что соберётся:
$ git clone [email protected]:safinaskar/libsh.git
$ cd libsh
$ ./dev.mk
$ mkdir ../libsh-build
$ cd ../libsh-build
$ cmake -DCMAKE_INSTALL_PREFIX=/путь/куда/ставить ../libsh
$ make
$ make install # Если /путь/куда/ставить требует прав рута, то эту команду нужно запускать от рута
Установку на Windows я не описываю, так как не совсем понятно, куда и зачем нужно этот самый libsh устанавливать. В Program Files
что ли? (Впрочем, если вы со мной не согласны, смело пишите мне свой feedback [e-mail в конце], мне бы очень хотелось узнать, как устроена культура установки маленьких проектов свободного ПО на нативном Windows, куда их принято устанавливать и принято ли вообще.) Итак, сборка на Windows. У вас должен быть установлен cmake версии как минимум 3.1.0 (он вышел совсем недавно, убедитесь, что у вас как минимум 3.1.0!) и Visual C++. Идём в https://github.com/safinaskar/libsh/releases , скачиваем последний libsh-extended-%VERSION%.zip (из git'а собрать не сможете, разумеется, под %VERSION% я подразумеваю конкретный номер версии), распаковываем, открываем Visual C++, там открываем консоль Visual C++ и в неё набираем:
> cd \путь\к\libsh-%VERSION%
> mkdir ..\libsh-build
> cd ..\libsh-build
> cmake ..\libsh-%VERSION%
> cmake --build .
Перед работой с библиотекой нужно запустить функцию sh_init
с именем программы (например, взятым из argv[0]
).
Исключение бросается с помощью SH_THROW
. Исключения в libsh пустые, т. е. они не несут никакой информации. Можно сказать, что исключение в libsh - это просто сигнал из точки возникновения исключения в точку его ловли. Поэтому исключение бросается попросту так:
SH_THROW;
Никаких аргументов к SH_THROW
не надо.
Есть конструкция SH_CTRY { ... } SH_CATCH { ... } SH_CEND;
. Она соответствует обычной try/catch-конструкции из C++, Java и C#. SH_CATCH
- это те действия, которые должны быть выполнены ровно в случае возникновения исключения в SH_CTRY
. Если в SH_CTRY
будет брошено исключение, управление передастся блоку SH_CATCH
, а после его выполнения продолжится обычный ход программы. В противном случае блок SH_CATCH
будет пропущен.
Есть конструкция SH_FTRY { ... } SH_FINALLY { ... } SH_FEND;
. Она соответствует try/finally из Java и C#. SH_FINALLY
- это действия, которые должны быть выполнены в любом случае, даже в случае возникновения исключения в SH_FTRY
. Если в SH_FTRY
будет брошено исключение, управление передастся блоку SH_FINALLY
, а после его выполнения исключение будет брошено опять. В противном случае блок SH_FINALLY
всё равно будет выполнен, но исключение в его конце брошено не будет.
Если исключение не будет поймано, будет сделан exit (EXIT_FAILURE)
. Это действие можно поменять с помощью sh_set_terminate
.
Пример, как с помощью одних только этих конструкций организовать работу с ошибками (потом будут примеры, показывающие, как это сделать короче):
#define _POSIX_C_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <libsh.h>
int
main (int argc, char *argv[])
{
sh_init (argv[0]);
FILE *fp = fopen ("file", "w");
if (fp == NULL)
{
perror ("fopen");
SH_THROW;
}
SH_FTRY
{
if (fprintf (fp, "hello\n") < 0)
{
perror ("fprintf");
SH_THROW;
}
}
SH_FINALLY
{
if (fclose (fp) == EOF)
{
perror ("fclose");
SH_THROW;
}
}
SH_FEND;
exit (EXIT_SUCCESS);
}
В libsh есть обёртки вокруг стандартных функций C и POSIX, которые в случае ошибки пишут сообщение в stderr и бросают исключение (с помощью sh_set_err
можно заменить stderr на что-нибудь другое). Например, sh_x_fopen
делает fopen и, если это не удалось, пишет сообщение и бросает исключение. Ещё раз подчеркну, что действия происходят в следующем порядке:
- Делаем fopen
- В случае ошибки пишем сообщение (да, именно здесь, а не в точке ловли исключения, как это делается обычно в C++!)
- Бросаем исключение (пустое!)
- В другом месте программы это исключение ловится, т. е. мы попадаем в блок
SH_FINALLY
илиSH_CATCH
. Или, если исключение так никто и не поймал, выполняетсяexit (EXIT_FAILURE)
sh_x_fopen
реализована примерно так (настоящую реализацию можно посмотреть в funcs.c
, когда он сгенерируется):
FILE *
sh_x_fopen (const char *SH_RESTRICT pathname, const char *SH_RESTRICT mode)
{
FILE *result = fopen (pathname, mode);
if (result == NULL)
{
sh_throw ("fopen: " "%s", pathname); // sh_throw - это, можно сказать, fprintf + SH_THROW
}
return result;
}
Пример программы с использованием обёрток (она делает то же самое, что и предыдущая):
#define _POSIX_C_SOURCE 1
#include <stdlib.h>
#include <libsh.h>
int
main (int argc, char *argv[])
{
sh_init (argv[0]);
FILE *fp = sh_x_fopen ("file", "w");
SH_FTRY
{
sh_x_fprintf (fp, "hello\n");
}
SH_FINALLY
{
sh_x_fclose (fp);
}
SH_FEND;
exit (EXIT_SUCCESS);
}
Таким образом, в libsh принята следующая идеология: в точке возникновения ошибки мы знаем, что именно произошло. Т. е. если fopen свалился, мы знаем, что свалился именно fopen, а не какая-нибудь другая функция, мы знаем значение переменной errno. На основании всей этой информации мы пишем информативное сообщение в stderr. Дальнейшему коду, на мой взгляд, уже не надо знать, что именно произошло (ведь мы уже выдали информацию в stderr), поэтому далее бросается просто пустое исключение. Оно как бы говорит: "Случилось что-то плохое, но что именно - не важно, т. к. инфа уже выдана в stderr, поэтому осталось лишь сделать некий откат (выполнить блоки SH_FINALLY) и продолжить работу либо завершить её".
В C++ (и других языках) обычно всё принято по-другому: если сообщение пишется в stderr, оно пишется обычно в точке ловли исключения, а не бросания, и исключение содержит в себе информацию об ошибке.
Итак, почему же я сделал именно так? Что ж, так проще. Не надо думать, о том как хранить информацию об исключении, и что она должна собой представлять. В моих программах мне бывает нужна только такая (моя) упрощённая система обработки ошибок.
Такая необычная система работы с ошибками является самым спорным моментов в libsh, ваш feedback будет очень кстати (e-mail в конце). Если сможете мне привести пример, где мой подход не срабатывает - буду очень рад (желательно в простом C-подобном коде, с минимальным использованием фич C++ или вообще без них, в простом коде в стиле GNU coreutils, а не в энтерпрайзном коде с мощным ООП, паттернами и т. д.).
Также в libsh есть функции для типичных операций в UNIX-подобных системах, они находятся в etc.c
, там же смотрите документацию (например, sh_multicat
).
libsh можно использовать и в C++, но см. коммент "C vs C++ statement" в ex.c
.
Дальнейшую документацию см. в комментах, отмеченных ///
(в том числе //@ ///
) в коде, в начале funcs.in
и в строчках, помеченных "docs:" в funcs.in
.
В libsh есть ещё очень много того, что не описано в этом README. Обо всём об этом рассказано в других файлах, например, в etc.c
, просто на данный момент недостаточно подробно.
libsh недописана, есть фичи, которые я ещё хочу реализовать. Также, явно недописанны доки. Тем не менее, уже сейчас я рад любому feedback'у от вас. Пишите мне на [email protected] .