![]() |
![]() |
|
|
C++ обертка библиотеки readline
Author / Автор: Сергей СацкийPublication date / Опубликовано: 09.05.2006
|
// The wrapper is in a single header file.
#include "SReadline.h"
using namespace swift;
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main( int argc, char ** argv )
{
// First parameter is a file name to save / load the
// commands history. The second is a max number of stored commands
// Both parameters could be ommited. In this case the history size
// will be 64 and no save/restore operations will be performed.
SReadline Reader( "/tmp/.testhist", 32 );
// Prepare the list of my own completers
vector< string > Completers;
// The following is supported:
// - "identifiers"
// - special identifier %file - means to perform a file name completion
Completers.push_back( "command1 opt1" );
Completers.push_back( "command1 opt2" );
Completers.push_back( "command1 opt3 %file" );
Completers.push_back( "command2 opt4" );
Completers.push_back( "command2 opt5 %file %file" );
// Now register the completers.
// Actually it is possible to re-register another set at any time
Reader.RegisterCompletions( Completers );
// Now we can ask user for a line
string UserInput;
bool EndOfInput( false );
for ( ; ; )
{
// The last parameter could be ommited
UserInput = Reader.GetLine( "Please input your command> ", EndOfInput );
if ( EndOfInput )
{
cout << "End of the session. Exiting." << endl;
break;
}
cout << "User input: '" << UserInput << "'." << endl;
cout << "Press Ctrl+D for gracefull exit" << endl;
}
// The history could be saved to an arbitrary file at any time
Reader.SaveHistory( "/tmp/BackupFileJustInCase" );
// The history could be cleared
Reader.ClearHistory();
// And the history could be loaded at any time
Reader.LoadHistory( "/tmp/BackupFileJustInCase" );
return 0;
}
|
Протокол работы с программой может выглядеть так:
[swift@swifthome examples]# ./SReadlineExample Please input your command> command command1 command2 Please input your command> command1 opt opt1 opt2 opt3 Please input your command> command1 opt2 User input: 'command1 opt2'. Press Ctrl+D for gracefull exit Please input your command> End of the session. Exiting. [swift@swifthome examples]# |
В приведенном протоколе клавиша TAB нажималась для завершения поддерживаемых команд в строках 2 и 4.
Замечания:
Предположим, что разработчику удобно воспользоваться следующим контейнером для хранения завершителей:
typedef pair< string, int > Element; typedef list< Element > Container; |
(Здесь тип int в паре взят только для упрощения записи)
В таком случае потребуется предоставить преобразование от Element к string. Это можно сделать, например, так:
struct MyElement : public Element { operator string () const { return first; } MyElement( const string & Arg1, int Arg2 ) : Element( Arg1, Arg2 ) {} }; |
Затем
typedef list< MyElement > MyContainer;
|
И наконец
. . . MyContainer Completers; Completers.push_back( MyElement( "command1 opt1", 1 ) ); Completers.push_back( MyElement( "command1 opt2", 2 ) ); Completers.push_back( MyElement( "command1 opt3 %file", 3 ) ); Completers.push_back( MyElement( "command2 opt4", 4 ) ); Completers.push_back( MyElement( "command2 opt5 %file %file", 5 ) ); . . . Reader.RegisterCompletions( Completers ); . . . |
Подобный подход может быть полезен, если указатели на функции (или что-нибудь аналогичное), которые обрабатывают команды, хранятся в том же контейнере, что и описатели завершителей ввода.
Исходные тексты приведенного выше простого примера, примера с более сложными элементами контейнера заполнителей и doxygen документацию можно найти на сайте автора по адресу http://satsky.spb.ru
Обертка использует следующие библиотеки:
Будьте внимательны, SReadline не имеет средств защиты от некорректной работы в многопоточном окружении. Главная причина этого состоит в том, что библиотека readline, которую использует обертка, предлагает C интерфейс и один сеанс использования библиотеки readline предполагает неизвестное количество обратных вызовов функций обработчиков. Тем не менее, обертка может быть полезна во многих случаях, так как большому количеству приложений не требуется конкурентный интерактивный ввод из нескольких потоков.
Кроме того, не гарантируется правильная работа обертки в случае использования обертки из различных единиц компиляции. Это связано с тем, что данные находятся в анонимном пространстве имен в заголовочном файле, и каждая единица компиляции будет иметь свою копию данных. Решение этой проблемы - перемещение данных в отдельную единицу компиляции.
Код тестировался с gcc 3.4.4 на Linux. Будьте внимательны, в библиотеке libreadline версии 4.3 имеются утечки памяти. Они возникают каждый раз при получении сигнала SIGWINCH. Как только библиотека readline была обновлена до версии 5.1, утечки памяти прекратились. Скорее всего, код обертки будет работать правильно без каких - либо изменений и на других версиях UNIX.
Существуют, по крайней мере, две альтернативы: