Обучение и семинары
Новости
Статьи
Вопросы и ответы
Бесплатные семинары
Кафедра МФТИ
IT-Сертификация
Новости
Статьи
Экзамены
Каталог ПО
Лицензиатор ПО
Схемы лицензирования
Новости
Вопросы и ответы
Каталог DOWNLOAD
Форумы
Свободное ПО/Open Source
Каталог свободного ПО
Системы автоматизации
ERP-система iRenaissance
Документооборот
АСУ ТП Портал
О компании
Новости
Наши координаты
Программа партнерства
Наши вакансии
Новое на сайте
ИТ и кризис
ИТ-Юмор
ИТ-глоссарий
RSS-лента
Архив
 
Голосование
Вас интересует приобретение лицензий Oracle Database с бесплатной установкой?
 

Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости s-networks.ru - ПО, книги, документация, курсы обучения
СУБД Oracle "с нуля"
Программирование на Visual С++
Оптимизация и работа в Windows (2000/XP/Vista)
Экономическая и деловая литература. Рецензии и отзывы
Краткие описания программ и ссылки на них
 
Рассылки Maillist.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости s-networks.ru - ПО, книги, документация, курсы обучения
Оптимизация и работа в Windows (2000/XP/Vista)
 
Рассылки Mail.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости s-networks.ru - ПО, книги, документация, курсы обучения
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
Обсуждения в форумах
ищется удаленный .NET разработчик (3)
Украинскому филиалу американской компании Brauncommunication (http://brauncommunication.com)...
 
Поднять декомпозицию процесса на уровень выше (3)
Уважаемые, подскажите пожалуйста, есть модель IDEF0, - А0, А-0 - декомпозированная на...
 
Пишу программы на заказ профессионально (1)
Пишу программы на заказ на языках Pascal (численные методы, списки, деревья, прерывания) под...
 
70-671 экзмен на русском языке. (1)
Уже в третий раз пытался сдать экзамен MSP 70-671 на русском языке и все без результатно,...
 
Регистрация на Oracle.com (1)
Сразу прошу прощения за тупой вопрос, но вчера зарегался на oracle.com (чтоб 9i слить себе...
 
 
 




HOWTO: Свой поток ввода-вывода языка С++ (исходники)

Встань у реки, смотри, как течет река;
Ее не поймать ни в сеть, ни рукой.
Она безымянна, ведь имя есть лишь у ее берегов;
Забудь свое имя и стань рекой.

Борис ГребенщиковГребенщиков— русская фамилия. Происхождение фамилии— от Гребенщик— мастер, изготавливающий деревянные или костяные гребни для волос .
 

БиблиотекаБиблиотека (греч. , от «книга» и «место хранения»)— учреждение, собирающее и хранящее произведения печати и письменности для общественного пользования, а также осуществляющее справочно-библиографическую работу. В настоящее время всё более распространяются и входят в фонд библиотеки микрофиши, микрофильмы, диапозитивы, аудио и видеокассеты, также всё более широкое распространение получают электронные носители (CD-ROM, DVD-ROM). ввода-вывода языка С++ - достаточно спорноеСпорное — посёлок городского типа в Ягоднинском районе Магаданской области России. явление. Но, так или иначе, она существует, иногда используется, и надо как-то с этим жить.

Типичные сценарии работы с потоком - порождение и преобразование. Порождение - это, например, выдача в поток данных из сокета. Хороший пример преобразования - перекодирование (в base64, в другую кодировку, шифрованиеШифрование — способ преобразования открытой информации в закрытую и обратно. Применяется для хранения важной информации в ненадёжных источниках или передачи её по незащищённым каналам связи. Шифрование подразделяется на процесс зашифровывания и расшифровывания., архивирование). Ещё можно что-нибудь лишнее удалять (пробелыПробел— интервал между буквами, обозначающий границы слов во многих системах письменности. Функционально пробел принадлежит к знакам препинания., комментарии), а что-нибудь нужное добавлять (разворачивать макросыМкрос (от греч. - большой, долгий;) — программный объект, который во время вычисления заменяется на новый объект, создаваемый определением макроса на основе его аргументов, затем выражается обычным образом.). Но при ближайшем рассмотрении оказывается, что преобразование - это частный случай порождения, когда данные "порождаются" не "из сокета", а на основе исходного потока. И, в итоге, всё сводится к созданию потока со стандартным s-networksом и своим собственным источником данных.

Постановка задачи проста и логична, разработчики библиотеки iostream, конечно, о ней догадывались и даже предприняли некоторые шаги… Нужно только понять, какие. Итак, задачка на reverse engineering: есть куча кода (реализация библиотеки), требуется понять, как он работает и что хотели сказать авторы.

Собственно, всё, что они хотели сказать, они сказали самой библиотекой, но в ней слишком много сюжетных линий, которые пересекаются удивительным образом. Понять ее целиком может только компилятор, а нам нужен один маленький кусочек…

Искать смысл будем в несколько идеализированных исходниках из VC2005, искать честно: почти половину статьи составляет скопированный код iostream :) Главное - выкинуть лишнее и расставить оставшееся в нужном порядке.

ПРИМЕЧАНИЕ

Конечный результатРезультат— заключительное последствие последовательности действий или событий, выраженных качественно или количественно. Возможные результаты включают преимущество, неудобство, выгоду, потерю, ценность и победу. проверялся в VC2005 и в gcc 4.2. STLPort отличается несущественно, а публичные и защищённые методы вообще прописаны в стандарте и должны совпадать во всех реализациях.

Идеализация - удалены некоторые незначимые мелочи…

Конечно, я уже знаю "правильный ответ", и мог бы объяснить его как-нибудь короче, понятнее и систематичнее… Но мне кажется более интересным провести вас тем же путём, которым шёл я сам, по сторонам открываются потрясающие виды. Следуйте за мной.

Все круги istream

 И я тебе скажу в свою чреду:
Иди за мной, и в вечные селенья
Из этих мест тебя я приведу,
И ты услышишь вопли исступленья
И древних духов, бедствующих там,
О новой смерти тщетные моленья;

Данте АлигьериДанте Алигьери (итал.Dante Alighieri), полное имя Дуранте дельи Алигьери (май/июнь 1265— 13 или 14 сентября 1321)— итальянский поэт, один из основоположников литературного итальянского языка. Создатель «Комедии» (позднее получившей эпитет «Божественной», введённый Боккаччо), в которой был дан синтез позднесредневековой культуры., 
перевод Михаила Лозинского
 

Начнём решать задачу с изучения обстановки.

ГенеалогияГенеалогия или родословие (др.-греч. — родословная, от (genea)— «семья» и (logos)— «слово, знание»)— систематическое собрание сведений о происхождении, преемстве и родстве фамилий и родов; в более широком смысле— наука о родственных связях вообще.

Во-первых, istream это вообще не класс, а всего лишь typedef.

typedef basic_istream<char, char_traits<char> > istream;

Реальный класс называется basic_istream:

template<class _Elem, class _Traits>
class basic_istream: virtual public basic_ios<_Elem, _Traits>
{  // controlCtrl (сокращение от Control, произносится /kntrl/)— системная кнопка (клавиша) на компьютерной клавиатуре. extractions from a stream buffer

Он унаследован от:

template<class _Elem, class _Traits>
class basic_ios: public ios_base
{  // base class for basic_istream/basic_ostream

А тот от:

class ios_base : public _Iosb<int>
{  // base class for ios

Ну и наконец:

template<class _Dummy>
class _Iosb
{  // define templatized bitmask/enumerated types, instantiate on demand

ПРИМЕЧАНИЕ

Класс _Iosb - Microsoft specific, такая особенность реализации, ни на что существенное не влияет. Остальные прописаны в стандарте.

Обобщённые символы

В соответствии с идеологией STL, поток работает с обобщёнными символами. Конкретизация потока получает в параметрах шаблона класс символов и пачку методов для работы с ними. "Пачка методов" выглядит примерно так:

template<class _Elem>
struct char_traits
{
  typedef _Elem char_type;
  typedef long int_type;
  typedef streampos pos_type;
  typedef streamoff off_type;
  typedef _Mbstatet state_type;

  static void assign(_Elem& _Left, const _Elem& _Right);
  static bool eq(const _Elem& _Left, const _Elem& _Right);
  static bool lt(const _Elem& _Left, const _Elem& _Right);

  static int compare(const _Elem *_First1, const _Elem *_First2, size_t _Count);
  static size_t length(const _Elem *_First);
  static _Elem* copy(_Elem *_First1, const _Elem *_First2, size_t _Count);
  static const _Elem* find(const _Elem *_First, size_t _Count, const _Elem& _Ch);
  static _Elem* move(_Elem *_First1, const _Elem *_First2, size_t _Count);
  static _Elem* assign(_Elem *_First, size_t _Count, _Elem _Ch);

  static _Elem to_char_type(const int_type& _Meta);
  static int_type to_int_type(const _Elem& _Ch);
  static bool eq_int_type(const int_type& _Left, const int_type& _Right);
  static int_type eof();
  static int_type not_eof(const int_type& _Meta);
};

И вместо стандартных функций и операторов ==, >, < реализация потока честно использует именно эти методы. Немного длиннее и выглядит странно, зато одинаково успешно работает с char и с wchar_t (для них сделаны явные специализации char_traits). Ещё можно делать вот такие бесполезные штуки:

#include <fstream>

namespace std
{
  // если не определить свой char_traits, всё
  // будет преобразовываться к int-у, дробные части потеряются
  template<> struct char_traits<float>
  {
    typedef float _Elem;
    typedef float char_type;
    typedef float int_type; 

    ... // сюда скопировать реализацию char_traits
  };

  typedef basic_fstream<float, std::char_traits<float> > ffstream;
}

int main()
{
  std::ffstream fs("test", std::ios_base::out);

  float f[10] = {1.1, 2.2, 3.3, -1, 5.5, 6.6, 7.7, 8.8, 9.9, 0};
  fs.write(f, 10); // Ну, это понятно
  fs << "test";    // Но и это тоже работает!
  fs << 3.1416;    // Угадайте, как работает вот это?
  fs << 3.14f;     // А если так?

  fs.close();
}

ПРИМЕЧАНИЕ

Это код для VC2005, и он более-менее рабочий.

В gcc char_traits реализован более гибко: используемые типы задаются структурой _Char_types, которую можно явно специализировать для float. Этот код компилируется и запускается, но почему-то совсем не работает, не разбирался почему.

Небольшая проблема заключается в существовании char_traits::eof(). ЗначениеЗначение— ассоциативная связь между знаком и предметом обозначения., являющееся признаком конца, не должно принадлежать char_type, иначе оно может неожиданно встретиться в середине файла. Именно для этого введён тип int_type: он должен включать в себя весь char_type и ещё хотя бы одно значение, которое можно будет объявить eof-ом.

В специализации char_traits для char сделано так:

typedef char _Elem;
typedef _Elem char_type;
typedef int int_type;

...

static int_type to_int_type(const _Elem& _Ch)
{  // convert character to metacharacter
  return ((unsigned char)_Ch);
}
static int_type eof()
{  // return end-of-file metacharacter
  return (EOF);  // определён как -1 - С.Х.
}

В результате eof это -1, а нормальные символы всегда преобразуются к положительным int-ам. Приведённая реализация специализации char_traits для float всего этого не учитывает, из-за чего поток не совсем корректно реагирует на -1 на входе.

Источник данных

Через некоторое время становится ясно, что basic_istream вовсе не абстрактныйАбстракция (от лат.abstractio— «отвлечение»)— отвлечение в процессе познания от несущественных сторон, свойств, связей предмета или явления с целью выделения их существенных, закономерных признаков; абстрагирование; теоретическое обобщение как результат такого отвлечения. базовый класс, в котором можно переопределить чисто виртуальный метод get, считывающий следующий символ. Вообще, во всей иерархии классов виртуальные - только деструкторы, других виртуальных методов не наблюдается.

Зато есть несколько не виртуальных get-ов, самый простой из них выглядит так:

// extract a metacharacter
int_type get()
{
  int_type _Meta = 0;
  ios_base::iostate _State = ios_base::goodbit;
  _Chcount = 0;
  const sentry _Ok(*this, true);

  if (!_Ok)
    _Meta = _Traits::eof();  // state not okay, return EOF
  else
  {
    // state okay, extract a character
    _TRY_IO_BEGIN
    _Meta = _Myios::rdbuf()->sbumpc(); <-- Смотреть сюда!

    if (_Traits::eq_int_type(_Traits::eof(), _Meta))
      _State /= ios_base::eofbit / ios_base::failbit;  // end of file
    else
      ++_Chcount;  // got a character, count it
    _CATCH_IO_END
  }

  _Myios::setstate(_State);
  return (_Meta);
}

Метод rdbuf определён в классе basic_ios:

template<class _Elem, class _Traits>
class basic_ios : public ios_base
{  
public:

  typedef basic_streambuf<_Elem, _Traits> _Mysb;

  ...

  _Mysb* rdbuf() const
  {
    // return stream buffer pointer
    return (_Mystrbuf);
  }

private:
  ...

  _Mysb *_Mystrbuf;  // pointerУказатель (поинтер, англ.pointer)— переменная, диапазон значений которой состоит из адресов ячеек памяти и специального значения— нулевого адреса. Значение нулевого адреса не является реальным адресом и используется только для обозначения того, что указатель в данный момент не может использоваться для обращения ни к какой ячейке памяти. to stream buffer
};

Для того чтобы окончательно заинтересоваться классом basic_streambuf осталось привести код основного конструктора класса basic_istream:

// constructConstruct Classic(раннее Scirra Construct) — это конструктор двумерных игр с открытым исходным кодом для Windows (использует DirectX 9). from stream buffer pointer
explicit basic_istream(_Mysb *_Strbuf, bool _Isstd = false) : _Chcount(0)
{
  _Myios::init(_Strbuf, _Isstd);
}

ПРИМЕЧАНИЕ

Почему "основного"? Реализация basic_istream в VC2005 содержит ещё один конструктор:

basic_istream(_Uninitialized)

{

ios_base::_Addstd(this);

}

Судя по вызову _Addstd, его хотели использовать для стандартных потоков cin/cout/cerr, однако использовать всё же не стали (см. файл cout.cpp в исходниках crt из VC2005). Булев параметр _Isstd, видимо, тоже предназначался для стандартных потоков, но тоже не используется. В STLPort нет ни второго конструктора, ни второго параметра, в стандарте тоже.

Источник данных-II

ОптимистОптимизм (лат.optimus— наилучший)— взгляд на жизнь с позитивной точки зрения, уверенность в лучшем будущем. Оптимизм утверждает, что мир замечателен, из любой ситуации есть выход, всё получится хорошо, все люди в общем хорошие. начал бы разматывать basic_streambuf с того, на чём остановились, то есть со sbumpc. Но это слишком ненадёжный путь. Вместо этого ещё немного посмотрим, откуда basic_istream берёт данные.

ПРИМЕЧАНИЕ

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

Более интересный метод get:

// get up to _Count characters into NTCS, stop before _Delim
_Myt& get(_Elem *_Str, streamsize _Count, _Elem _Delim)
{	
  ios_base::iostate _State = ios_base::goodbit;
  _Chcount = 0;

  // extract characters
  int_type _Meta = _Myios::rdbuf()->sgetc(); <-- (1)

  for (; 0 < --_Count; _Meta = _Myios::rdbuf()->snextc()) <-- (2)
    if (_Traits::eq_int_type(_Traits::eof(), _Meta))
    {  // end of file, quit
      _State /= ios_base::eofbit;
      break;
    }
    else if (_Traits::to_char_type(_Meta) == _Delim)
      break; // got a delimiter, quit
    else
    {  // got a character, add it to string
      *_Str++ = _Traits::to_char_type(_Meta);
      ++_Chcount;
    }

  _Myios::setstate(_Chcount == 0 ? _State / ios_base::failbit : _State);
  *_Str = _Elem(); // add terminating null character
  return (*this);
}

ПРИМЕЧАНИЕ

Обратите внимание на возвращаемое значение: это ссылка на себя. Чтобы получить количество прочитанных символов, нужно вызвать метод gcount.

Метод read:

_Myt& read(_Elem *_Str, streamsize _Count)
{
  return _Read_s(_Str, (size_t)-1, _Count);
}

_Read_s придумана программистами из Microsoft и реализована так:

// read up to _Count characters into buffer
_Myt& _Read_s(_Elem *_Str, size_t _Str_size, streamsize _Count)
{	
  ios_base::iostate _State = ios_base::goodbit;
  _Chcount = 0;                /-- вот тут
                               V
  const streamsize _Num = _Myios::rdbuf()->_Sgetn_s(_Str, _Str_size, _Count);
  _Chcount += _Num;
  if (_Num != _Count)
    _StateСостояние (англ.State)— шаблон проектирования. Используется в тех случаях, когда во время выполнения программы объект должен менять свое поведение в зависимости от своего состояния. /= ios_base::eofbit / ios_base::failbit; // short read

  _Myios::setstate(_State);
  return (*this);
}

СуффиксСуффикс (от лат.suffixus «прикреплённый») в лингвистике— морфема, изменяемая часть слова, расположенная обычно после корня. В российской школьной традиции суффикс в слове схематически выделяется значком ^ Нулевой суффикс зачастую отмечается значком (компьютерный ноль). "_s" образован от слова "secure". Подразумевается, что, раз ей передаётся на один размер больше, она более безопасная, некоторые в это верят. _Sgetn_s - это тоже идея Microsoft, в стандартной реализации basic_streambuf её нет. Но это мы забегаем вперёд.

ПРИМЕЧАНИЕ

Я не имею в виду, что все _s-функции бесполезны, я все не смотрел. Но в данном случае… ФункцияФункция— многозначный термин, который означает такое отношение между элементами, в котором изменение в одном влечет изменение в другом. принимает на вход два числа: размер буфера и число считываемых символов. Как вы думаете, что она делает? Правильно, выбирает меньшее из двух. Не вижу, почему этого не может сделать вызывающий код. Не понимаю, на что он вообще может рассчитывать, пытаясь прочитать больше, чем влезает.

Метод рeek:

// return next character, unconsumed
int_type peek()
{  
  ios_base::iostate _State = ios_base::goodbit;
  _Chcount = 0;
  int_type _Meta = 0;

  if (_Traits::eq_int_type(_Traits::eof(),
          _Meta = _Myios::rdbuf()->sgetc())) <-- Ага!
    _State /= ios_base::eofbit;

  _Myios::setstate(_State);
  return (_Meta);
}

Похожим образом устроены putback, unget, sync, seekg, и tellg: они просто передают управлениеУправление— воздействие субъекта, направленное на достижение абстрактной (неконкретной), но вынужденно-корректируемой цели (задачи, идеи) в уже сложившихся рамках правил, которые неизбежно-совершенствуются когда субъект непротиворечивее познаёт реальность, с которой сосуществует. соответствующим им sputbackc, sungetc, pubsync, pubseekpos, pubseekoff.

Один из встроенных операторов >>:

typedef istreambuf_iterator<_Elem, _Traits> _Iter;
typedef num_get<_Elem, _Iter> _Nget;

...

// extract an int
_Myt& operator>>(int& _Val)
{
  ios_base::iostate _State = ios_base::goodbit;

  long _Tmp = 0;
  const _Nget& _Nget_fac = _USE(ios_base::getloc(), _Nget);

  _Nget_fac.get(_Iter(_Myios::rdbuf()), _Iter(0), *this, _State, _Tmp);

  if (_State & ios_base::failbit // _Tmp < INT_MIN // INT_MAX < _Tmp)
    _State /= ios_base::failbit;
  else
    _Val = _Tmp;

  _Myios::setstate(_State);
  return (*this);
}

Мы не будем углубляться в реализацию istreambuf_iterator, тем более что он использует уже встречавшиеся sbumpc и sgetc. А num_get, в реализацию которого мы тоже углубляться не будем, просто использует итератор.

Ну и хватит. Что у нас получилось:

  • sbumpc - считывает символ, переходит к следующему (используется в первом get).
  • sgetc - считывает символ, не переходит к следующему (используется в peek и ещё много где).
  • snextc - переходит к следующему и возвращает его (используется во втором get).
  • _Sgetn_s - считывает строку символов указанной длины (используется в _Read_s).
  • sputbackc, sungetc - возврат символа обратно в поток
  • pubsync - непонятно что.
  • pubseekpos, pubseekoff - изменение текущей позиции в потоке.

Возможно, что-то мы пропустили, но некоторое пониманиеПонимание — психологическое состояние, верное восприятие или интерпретация какого-либо события, явления, факта, принятое в определенном кругу. того, как работает basic_streambuf уже должно сложиться.

По болотам basic_streambuf

 - Давайте отрежем Сусанину ногу.
- Не надо, ребята, я вспомнил дорогу!

фольклор
 

К сожалению, идея, заложенная в класс basic_streambuf, не выводится очевидным образом из его реализации, поэтому здесь мне не удастся ограничиться копированием в статью исходного кода; придётся и самому что-то написать.

Предлагаемая модельМодель (фр.modle, от лат.modulus— «мера, аналог, образец»)— некоторый материальный или мысленно представляемый объект или явление, являющийся упрощённой версией моделируемого объекта или явления (прототипа) и в достаточной степени повторяющий свойства, существенные для целей конкретного моделирования(опуская несущественные свойства, в которых он может отличаться от прототипа). функционирования наследника basic_streambuf:

  • Есть некоторый буфер "данных для чтения". Есть его начало, конец, указатель на текущий символ.
  • Операции чтения получают данныеДанные (калька от лат.data) — это представление фактов и идей в формализованном виде, пригодном для передачи и обработки в некотором информационном процессе. из этого буфера, при этом сдвигают указатель вперёд. Операции чтения-без-сдвига - соответственно, не сдвигают. Операции putback/ungetc сдвигают указатель обратно.
  • Все эти операции могут неожиданно дойти до какого-то края буфера. В этом случае вызывается обработчик для соответствующего типа выхода за пределы буфера.
  • Обработчики - защищённые виртуальные методы, публичных виртуальных методов нет (кроме деструктора, но он не вполне "метод" на мой взгляд).
  • И для записи всё то же самое.
  • И ещё есть несколько защищённых виртуальных методов для разных других ситуаций.

ПРИМЕЧАНИЕ

По GoF, использованный паттернПаттерн (англ.pattern— образец, шаблон, система)— заимствованное слово. Слово «pattern» используется как термин в нескольких западных дисциплинах и технологиях, откуда оно и проникло в русскоязычную среду. Смысл термина «паттерн» всегда уже чем просто «образец», и варьируется в зависимости от области знаний, в которой используется. проектирования называется Template Method; его же часто называют Non-Virtual s-networks.

Спасибо за напоминание названия паттерна и несколько других важных поправок.

Для простоты буферизацию можно отключить, а большую часть "других ситуаций" игнорировать. Если в качестве буфера установить 0, все операции будут "выходить за край", то есть каждый раз будут вызываться обработчики; осталось только правильно их реализовать. Этим и займёмся, к буферам и прочему вернёмся позже.

Простое чтение, методы

Сначала - публичный s-networks. Выбросим typedef-ы, работу с буферами, позиционирование, локализации и всё остальное, что нас сейчас не интересует:

// control read/write buffers
template<class _Elem, class _Traits>
class basic_streambuf
{
public:
  ...

  // Get area:
  int_type sgetc();
  int_type sbumpc();
  int_type snextc();
  streamsize sgetn(_Elem *_Ptr, streamsize _Count);
  // MS specific
  streamsize _Sgetn_s(_Elem *_Ptr, size_t _Ptr_size, streamsize _Count);

  ...
};

ПРИМЕЧАНИЕ

Заодно мы выбросили putback, к нему тоже вернёмся позже.

Реализация putback без буферов - ненужный ручной труд, чреватый ошибками; при правильном использовании буферов всё получается само.

sgetc

Возвращает текущий символ и оставляет указатель на месте. Вызов sgetc в цикле должен всё время возвращать одно и то же.

int_type sgetc()
{  // get a character and don't point past it
  return (0 < _Gnavail() ? _Traits::to_int_type(*gptr()) : underflow());
}

На краю буфера вызывается underflow:

virtual int_type underflow();

Это первый переопределяемый метод.

sbumpc

Возвращает текущий символ и передвигает указатель вперёд. Вызов sbumpc в цикле должен последовательно прочитать все доступные символы.

int_type sbumpc()
{  // get a character and point past it
  return (0 < _Gnavail() ? _Traits::to_int_type(*_Gninc()) : uflow());
}

Если буфер кончился, вызывается unflow:

virtual int_type uflow();

Это второйВторой — второй по счёту альбом песен Владимира Высоцкого в исполнении Григория Лепса, записанный и вышедший в 2007 году переопределяемый метод.

snextc

Передвигает указатель вперёд и возвращает новый текущий символ. Вызов snextc в цикле должен последовательно прочитать все доступные символы кроме первого. Предназначен для использования в паре с sgetc.

int_type snextc()
{  // point to next character and return it
  return (1 < _Gnavail()
        ? _Traits::to_int_type(*_Gnpreinc())
         : _Traits::eq_int_type(_Traits::eof(), sbumpc())
             ? _Traits::eof() : sgetc());
}

Логика работы:

  • Если до конца буфера больше одного символа, то перейти к следующему и вернуть его.
  • Иначе, вызвать sbumpc, если она вернула traits_type::eof(), вслед за ней вернуть traits_type::eof().
  • Иначе вернуть результат sgetc.

Никаких обработчиков напрямую не вызывается, достаточно корректно реализовать sbumpc и sgetc.

sgetn, _Sgetn_s

Пытаются скопировать по переданному адресу заданное количество символов, возвращают количество реально скопированных.

streamsize sgetn(_Elem *_Ptr, streamsize _Count)
{  // get up to _Count characters into array beginning at _Ptr
  return xsgetn(_Ptr, _Count);
}

streamsize _Sgetn_s(_Elem *_Ptr, size_t _Ptr_size, streamsize _Count)
{  // get up to _Count characters into array beginning at _Ptr
  return _Xsgetn_s(_Ptr, _Ptr_size, _Count);
}

Вызывают xsgetn и _Xsgetn_s соответственно:

virtual streamsize xsgetn(_Elem* _Ptr, streamsize _Count)
{  // get _Count characters from stream
  // assume the destination buffer is large enough
  return _Xsgetn_s(_Ptr, (size_t)-1, _Count);
}

virtual streamsize _Xsgetn_s(_Elem* _Ptr, size_t _Ptr_size, streamsize _Count)
{ 
  ... 
}

Для корректной работы стандартных реализаций достаточно sbumpc, ничего нового они не вызывают. Переопределять имеет смысл, только если можно увеличить эффективность, мы не будем этим заниматься.

СОВЕТ

Обратите вниманиеВнимание— избирательная направленность восприятия на тот или иной объект.: реализация basic_istream от MicrosoftMicrosoft (Microsoft Corporation, читается «майкрософт», NASDAQ: MSFT)— одна из крупнейших транснациональных компаний по производству программного обеспечения для различного рода вычислительной техники— персональных компьютеров, игровых приставок, КПК, мобильных телефонов и прочего, разработчик наиболее широко распространённой на данный момент в мире программной платформы— семейства операционных систем Windows. не вызывает sgetn вообще, а значит, и ваша эффективная xsgetn практически не принесет пользы. Придётся переопределить _Xsgetn_s, специально для MS.

Простое чтение, обработчики

Выявлены:

  • underflow;
  • uflow;
  • xsgetn, _Xsgetn_s - переопределять не обязательно.

Примерно на десять минут работы. Вот такой полезный вспомогательный класс:

template <typename ch, typename tr> 
class basic_symbol_istreambuf: public std::basic_streambuf<ch, tr>
{
public:

  typedef std::basic_streambuf<ch, tr> base;
  // Иначе их не видит gcc
  typedef typename base::int_type int_type;
  typedef typename base::traits_type traits_type;

  basic_symbol_istream«Домашний Интернет и Телевидение МТС»— торговая марка, под которой компания «МТС» предоставляет в России услуги кабельного телевидения и широкополосного доступа в интернет.buf() : _ready(false)
  {
    // Отказываемся от буферов
    setg(0, 0, 0);
    setp(0, 0);
  }

protected:

  // Иначе их не видит gcc
  using base::setg;
  using base::setp;

  // Определит потомок
  // Возвращает либо traits_type::eof(), либо traits_type::to_int_type(c)
  virtual int_type readChar() = 0;

  // Реализация underflow
  virtual int_type underflow()
  {
    if (_ready)
    {
      // Текущий символ уже прочитан
      return _char;
    }

    _char = readChar();
    _ready = true;

    return _char;
  }

  // Реализация uflow
  virtual int_type uflow()
  {
    if (_ready)
    {
      // Текущий символ уже прочитан
      if (_char != traits_type::eof())
      {
        // И он не последний - нужно "перейти к следующему"
        _ready = false; 
      }

      return _char;
    }

    // Текущий символ ещё не прочитан
    return readChar();
  }

private:

  int_type _char;
  bool _ready;
};

А теперь:

class random_buf 
  : public basic_symbol_istreambuf<char, std::char_traits<char> >
{
public:

  random_buf()
  {
    srand(time(0));
  }

  int readChar()
  {
    return traits_type::to_int_type(rand());
  }
};

int main()
{
  random_buf rb;
  std::istream st(&rb);

  std::string c;
  st >> c; // Читает до первого пробельного символа

  std::cout << c << '\n';
}

Вуаля! Создание потокаПотока (болг. Потока) — село в Болгарии. Находится в Смолянской области, входит в общину Смолян. Население составляет 11 человек. выглядит немного неуклюже, но программаПрограмма - (от греч. — пред, греч. — запись) термин, в переводе означающий «предписание», т.е. работает.

Через буферы к звёздам

 With our full crew a-board 
And our trust in the Lord 
We're comin' in on a wing and a prayer
 
Harold Adamson
 

Для работы с буфером данных-для-чтения нам доступны следующие функции:

protected:
  void setg(_Elem *_First, _Elem *_Next, _Elem *_Last); // Инициализация

  _Elem *eback() const; // Начало буфера
  _Elem *gptr() const; // Текущий символ
  _Elem *egptr() const; // Конец
  void gbump(int _Off); // Сдвигает позицию текущего символа

Имена функций - это головоломка:

* "g" в setg, gptr, egptr и gbump - от "get"

* "e" в eback и egptr - от "end"

Идея в том, что у буфера есть "текущее положение" и два способа передвижения: вперёд и назад. Передвижение любым из способов когда-нибудь упирается в край буфера, соответственно, у буфера есть два конца: конец для движения вперёд и конец для движения назад; egptr и eback. "Начало" - в текущей точке, здесь-и-сейчас.

Логика образования имён этих функций была открыта мне , спасибо ему.

Можно считать, что setg присваивает значение указателям, значения которых возвращают eback, gptr и egptr. Что ещё нужно отметить:

  • setg можно вызывать сколько угодно раз.
  • За выделением-освобождением памяти нужно следить самостоятельно.

Хорошие новостиНовости— программа (собрание нескольких новостей) на телевидении и радио. В печатной прессе новостями также называются сводки новостей, например, в специальной рубрике в газете.

В общем-то, с буфером даже проще. Да, нужно запомнить ещё несколько функций, но зато, как выясняется, они внесены в s-networks basic_streambuf не случайно, он был рассчитан именно на такое использование.

Например, обработчик uflow имеет стандартную реализацию, вот она:

virtual int_type uflow()
{ // get a character from stream, point past it
  return (_Traits::eq_int_type(_Traits::eof(), underflow())
    ? _Traits::eof() : _Traits::to_int_type(*_Gninc()));
}

Логика работы:

  • Вызвать underflow.
  • Если underflow вернула traits_type::eof(), тоже вернуть traits_type::eof().
  • Иначе вернуть первый элемент буфера и передвинуть указатель вперёд.

Здесь неявно подразумевается, что если underflow что-то успешно прочитала, то она заносит это в буфер. И если это действительно так, uflow можно не переопределять, стандартная прекрасно работает.

ДругойДругой — центральная категория современной философии. Актуализация данного понятия связана с такими событиями, как антропологический и лингвистический поворот. Другой — это не Я, тот, кто противостоит мне, находится по ту сторону меня, моих ценностей, моего мировоззрения. И вместе с тем, Другой такой же как Я: он мыслит, чувствует, ходит и т. д. пример - реализация putback/unget. В basic_streambuf им соответствуют sputbackc и sungetc:

int_type sputbackc(_Elem _Ch)
{  // put back _Ch
  return (gptr() != 0 && eback() < gptr() && _Traits::eq(_Ch, gptr()[-1])
         ? _Traits::to_int_type(*_Gndec())
         : pbackfail(_Traits::to_int_type(_Ch)));
}

int_type sungetc()
{  // back up one position
  return (gptr() != 0 && eback() < gptr()
        ? _Traits::to_int_type(*_Gndec()) : pbackfail());
}

Переопределяемый обработчик выхода за пределы - pbackfail:

virtual int_type pbackfail(int_type c = traits_type::eof());

Но стандартная реализация вполне справляется без него до тех пор, пока:

  • Позиция текущего символа не сдвинулась на передний край буфера.
  • Символ, "возвращаемый" в поток, совпадает с предыдущим прочитанным символом в буфере.

Надо ли разрешать пользователю больше - вопросВопрос— форма мысли, выраженная в языке предложением, которое произносят или пишут, когда хотят что-нибудь спросить, то есть узнать какую-то информацию. Если вопрос произносят, то используют вопросительную интонацию, а если пишут, то в конце ставят вопросительный знак. Задающий вопрос обычно ожидает ответ. Исключение составляет риторический вопрос, на который ответ не требуется. философский, если не надо, то и делать ничего не придётся.

Код

Из двух обязательных для переопределения функций осталась одна - underflow. В классе basic_buffered_istreambuf её нужно переопределить так, чтобы она:

  • вызывала переопределяемую потомком функцию readData для чтения очередного куска данных;
  • записывала прочитанное в буфер;
  • вызывала setg и устанавливала указатели в правильное место;
  • возвращала первый из прочтённых символов.

Примерно так:

// Определит потомок
// Возвращает либо количество прочитанного, либо -1
virtual int readData(char_type* buffer, size_t length) = 0;

// Реализация underflow
virtual int_type underflow()
{    
  // читаем новую порцию
  int symbols_read = readData(_buffer, buffer_size);

  if (symbols_read <= 0)
  {
    // не вполне удачно
    setg(_buffer, _buffer, _buffer);
    return traits_type::eof();
  }

  // удачно!
  setg(_buffer, _buffer, _buffer + symbols_read);

  // возвращаем текущий символ, не сдвигая указатель
  return traits_type::to_int_type(*egptr());
}

Недостаток этого решения в том, что сразу после вызова такой underflow перестаёт работать стандартная реализация putback: некуда "отступить", текущий элемент находится в самом начале буфера. Это неприятно, пусть мы и ограничиваем глубину putback, но хочется, чтобы он всегда«Всегда» — кинофильм. Детям рекомендуется просмотр совместно с родителями. более-менее работал.

Модифицированная версия (а заодно буфер засунули в вектор, по морально-этическим соображениям):

template <typename ch, typename tr> 
class basic_buffered_istreambuf: public std::basic_streambuf<ch, tr>
{
public:

  typedef std::basic_streambuf<ch, tr> base;
  // Иначе их не видит gcc
  typedef typename base::int_type int_type;
  typedef typename base::traits_type traits_type;
  typedef typename base::char_type char_type;

  basic_buffered_istreambuf(size_t size = 512, size_t back = 10) 
    : buffer_size(size), back_size(back)
  {
    _buffer.resize(back_size + buffer_size);

    setg(0, 0, 0);
    setp(0, 0);
  }

protected:

  // Иначе их не видит gcc
  using base::setg;
  using base::setp;
  using base::eback;
  using base::gptr;
  using base::egptr;

  // Определит потомок
  // Возвращает либо количество прочитанного, либо -1
  virtual int readData(char_type* buffer, size_t length) = 0;

  // Реализация underflow
  virtual int_type underflow()
  {    
    size_t offset = 0;
      
    if (eback() != egptr())
    {
      // обеспечиваем себе putback
      // глубинаГлубина— расстояние от поверхности водоёма до его дна. Средняя глубина мирового океана— 3790м, а самая глубокая впадина— Марианская (11022м). не больше, чем:
      // -- количество прочитанных символов
      // -- константа back_size
      offset = std::min<size_t>(back_size, egptr() - eback());
      memmove(&_buffer[0], eback() - offset, offset);
    }

    // читаем новую порцию
    int symbols_read = readData(&_buffer[offset], buffer_size);

    if (symbols_readed <= 0)
    {
      // не вполне удачно
      base::setg(&_buffer[0], &_buffer[offset], &_buffer[offset]);
      return traits_type::eof();
    }

    // удачно!
    base::setg(&_buffer[0], 
          &_buffer[offset], 
          &_buffer[offset + symbols_read]);

    // возвращаем текущий символ не сдвигая указатель
    return traits_type::to_int_type(*gptr());
  }

private:

  std::vector<char_type> _buffer;
  const size_t buffer_size;
  const size_t back_size;
};

Использовать так же, как версию без буфера.

СОВЕТ

Кстати, basic_symbol_istreambuf полезно переписать с использованием буфера, для этого надо сделать buffer_size равным 1. Из плюсов: не нужно будет переопределять uflow, бесплатно заработает putback/unget.

… девушки?

 Потому, потому что мы пилоты,
Небо наш, небо наш родимый дом...

Соломон ФогельсонФогельсон (нем. Vogelson) — немецкая фамилия, дословно означает "птичий сын".
 

Наступило "потом", в общем-то, статья закончена. Но некоторые пропущенные мелочи хочется упомянуть, некоторые перспективы - продемонстрировать, коротко, в режиме "предупреждённый - вооружён". Девушки подождут ещё пять минут.

basic_streambuf::in_avail

Это последняя функция, относящаяся к чтению, она используется в basic_istream::readsome и возвращает количество символов, которые можно прочитать без задержек.

streamsize in_avail()
{  // return count of buffered input characters
  streamsize _Res = _Gnavail();
  return (0 < _Res ? _Res : showmanyc());
}

Она возвращает разницу между egptr и eback; если буфер отключен или пуст, возвращает результат вызова showmanyc.

virtual streamsize showmanyc();

ПРИМЕЧАНИЕ

Из стандарта: The morphemes of showmanyc are "es-how-many-see", not "show-manic".

Вы угадали, именно ради этого забавного комментария описание showmanic включено в статью :)

Что может возвращать showmanyc:

  • Неотрицательное число. Это количество символов, которое мы обещаем прочитать без задержек. Если 0 - ничего не обещаем.
  • -1. Это значит, что любое чтение вернёт traits_type::eof().

Реализация по умолчанию возвращает 0 и замечательно подходит для большинства применений.

УправлениеУправление— воздействие субъекта, направленное на достижение абстрактной (неконкретной), но вынужденно-корректируемой цели (задачи, идеи) в уже сложившихся рамках правил, которые неизбежно-совершенствуются когда субъект непротиворечивее познаёт реальность, с которой сосуществует. позицией

Стандартный поточный s-networks подразумевает последовательный доступ к данным, но, если поток способен на большее, iostream позволяет ему проявить себя. Для произвольного доступа предназначены методы pubseekoff и pubseekpos:

pos_type pubseekoff(off_type _Off, ios_base::seekdir _Way,
  ios_base::openmode _Mode = ios_base::in / ios_base::out)
{  // change position by _Off, according to _Way, _Mode
  return (seekoff(_Off, _Way, _Mode));
}

pos_type pubseekpos(pos_type _Pos,
  ios_base::openmode _Mode = ios_base::in / ios_base::out)
{  // change position to _Pos, according to _Mode
  return (seekpos(_Pos, _Mode));
}

Они реализованы через seekoff и seekpos:

virtual pos_type seekoff(off_type off, ios_base::seekdir way, 
  ios_base::openmode mode);
virtual pos_type seekpos(pos_type pos, ios_base::openmode mode);

Что интересного можно сказать про всю эту конструкцию:

  • Поскольку это не банальный C, понятие "текущая позиция" инкапсулирует класс traits_type::pos_type, и это честный класс, с определёнными в стандарте методами и операторами; но интересного в нём ничего нет. Несколько упрощая, можно сказать, что это обертка над 64-х разрядным числом.
  • Методы, заканчивающиеся на "off" ведут себя как обычные функции установки текущей позиции в файле: устанавливают позицию относительно некоторой точки, точка задаётся параметром "way". Методы, заканчивающиеся на "pos", устанавливают абсолютную позицию.
  • Параметр "mode" указывает, в котором потоке менять позицию: в потоке чтения или в потоке записи.
  • Реализация по умолчанию возвращает ошибку.

Запись

Почти всё, что говорилось про чтение, верно и для записи. Ну, там, конечно, другие имена методов, но в целом то же самое. Единственное существенное отличие - нужен аналог для функции flush. Для этого предназначен метод pubsync, который вызывает переопределяемый метод sync:

virtual int sync();

Возвращаемое значение: -1 при ошибке, что-то другое - при успехе.

Естественно, basic_ostream вызывает его из своего методаМетод (от греч. — «способ»)— систематизированная совокупность шагов, действий, которые необходимо предпринять, чтобы решить определенную задачу или достичь определенной цели. В отличие от области знаний или исследований, является авторским, то есть созданным конкретной персоной или группой персон, научной или практической школой. В силу своей ограниченности рамками действия и результата, методы имеют тенденцию морально устаревать, преобразовываясь в другие методы, развиваясь в соответствии с временем, достижениями технической и научной мысли, потребностями общества. Совокупность однородных методов принято называть подходом. Развитие методов является естественным следствием развития научной мысли. flush.

ПРИМЕЧАНИЕ

А вот зачем вызов pubsync/sync нужен в basic_istream - действительно непонятно :)

Исключения

В наше прогрессивное время сообщать о проблемах, возвращая -1, - это просто-таки ретроградство. Было бы странно, если бы библиотекаБиблиотека (греч. , от «книга» и «место хранения») — учреждение, собирающее и хранящее произведения печати и письменности для общественного пользования, а также осуществляющее справочно-библиографическую работу. В настоящее время всё более распространяются и входят в фонд библиотеки микрофиши, микрофильмы, диапозитивы, аудио- и видеокассеты, также всё более широкое распространение получают электронные носители (CD-ROM, DVD-ROM). с таким количеством шаблонов не поддерживала исключения. Конечно, она их поддерживает. Но не самым очевидным способом.

Модель следующая:

  • У потока есть состояние. Там выставляются флажки badbit, eofbit и т.п. Текущее состояние возвращается методом rdstate(), устанавливается методами clear() и setstate().
  • Ещё у потока есть "маска исключений". Возвращается методом exceptions(), устанавливается таким же методом, только с параметрПараметр (от др.-греч. «соразмеряю») — величина, значения которой служат для различения элементов некоторого множества между собой.ом.
  • Если в какой-то момент так получилось, что (rdstate() & exceptions() != 0), генерируется исключение типа ios_base::failure. Сверка должна происходить как при изменении состояния, так и при изменении маски.

Ну, и главное:

  • Если переопределённый пользователем метод basic_streambuf сгенерировал исключение, basic_istream должен его поймать, установить своё состояние в failbit и, если failbit присутствует в маске исключений, сгенерировать перехваченное исключение заново (а не ios_base::failure, как в обычной ситуации). Если failbit не присутствует в маске исключений, то исключение просто подавляется.

ПРИМЕЧАНИЕ

По стандарту все перечисленные здесь методыМетод (от греч. — «способ»)— систематизированная совокупность шагов, действий, которые необходимо предпринять, чтобы решить определенную задачу или достичь определенной цели. В отличие от области знаний или исследований, является авторским, то есть созданным конкретной персоной или группой персон, научной или практической школой. В силу своей ограниченности рамками действия и результата, методы имеют тенденцию морально устаревать, преобразовываясь в другие методы, развиваясь в соответствии с временем, достижениями технической и научной мысли, потребностями общества. Совокупность однородных методов принято называть подходом. Развитие методов является естественным следствием развития научной мысли. расположены в классе basic_ios, в Microsoft переместили их в ios_base и немного поменяли. ФункциональностьФункциональность (обычно в технике и программном обеспечении)— набор возможностей (функций), которые предоставляет данная система или устройство. сохранена.

Для стандартСтандарт (от англ.standard— норма, образец) в широком смысле слова— образец, эталон, модель, принимаемые за исходные для сопоставления с ними др. подобных объектов.ных потоков такая недоговорённость относительно типов исключений имеет существенный минус. Ни одна реализация stl не рискнёт генерировать какое-то "своё" исключение из кода fstream или stringstream - иначе рассчитывающий на него пользовательский код будет непереносимым, и вся "стандартность" библиотеки вылетит в трубу. В результате никаких внятных сообщений об ошибках стандартные классыКласс (от лат.classis— группа) в классификации— группа предметов или явлений, обладающих общими признаками. потоков предложить не могут: исключения использовать не получается, а других средств не предусмотрено.

Зато, если вы пишете свой поток, стандарт даёт зелёный свет: кидайте любые исключения, полная свободаСвобода— это наличие у человека или процесса возможности выбора варианта и реализации (обеспечения) исхода события. Отсутствие такого выбора и реализации выбора равносильно отсутствию свободы— несвободе. (см. также Степени свободы)..

ПроизводительностьПроизводительность— внесистемная величина, равная отношению объема проделанной работы к времени, за которое она была совершена.

Вообще-то, не очень. Во всяком случае, в VS2005 форматированный вывод в буфер при помощи sprintf работает примерно в три раза быстрее«Быстрее пули» (англ.Faster) — фильм-боевик режиссера Джорджа Тиллмана мл. Мировая премьера назначена на 4 ноября 2010 года (в России — 2 декабря)., чем stringstream. Замена stringstream на собственный класс потока не даёт видимого эффекта.

Boost it

Как было сказано в самом начале: "постановка задачи проста и логична"; и это действительно так, ничегоНичто— категория, фиксирующая отсутствие, небытие определённой сущности, или отсутствие, отрицание бытия вообще, активное начало негации, негативности вообще. нового я не придумал. В частности, разработчикам библиотеки boost похожие мысли тоже приходили в голову, и из них родилась . Простая, понятная, удобнаяУдобная— станица в Отрадненском районе Краснодарского края. Административный центр Удобненского сельского поселения., работающая.

Если обстоятельства непреодолимой силы не принуждают вас создавать собственный велосипедВелосипед (из фр.vlocipde, от лат.velox— быстрый и pes— нога, стопа)— транспортное средство, приводимое в движение мускульной силой человека через ножные педали или (редко) через ручные рычаги. Большинство велосипедов имеют два колеса, но бывают и трёх-, и четырёх-, и даже одноколёсные велосипеды., используйте boost. А всё, что было написано выше, можно забыть или даже не знать. Но лучше - знать и принимать к сведению.

Всё!

Теперь девушки :)

Но и о потоках тоже не забывайте.

Сергей Холодилов

Ссылки по теме

Магазин программного обеспечения   WWW.S-NETWORKS.RU
Microsoft Office Visio 2007 Russian
Crystal Reports Server 2008 Windows 5 Concurrent Access License
CaliberRM 2008 Named with Software and Maintenance
 
Другие предложения...
 
Курсы обучения   WWW.S-NETWORKS.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.S-NETWORKS.RU
 
Другие предложения...
 
Книжный магазин   WWW.S-NETWORKS.RU
Модель зрелости процессов разработки программного обеспечения - Capability Maturity Model for Software (CMM)
Simulation with arena with + CD
ИТ-аутсорсинг в России/IT outsourcing in Russia
 
Другие предложения...
 

 Рекомендовать »   Обсудить материал в конференции Дискуссии и обсуждения общего плана »
Написать редактору 
 Распечатать »
 Правила публикации »
Дата публикации: 11.08.2009 
Мы рекомендуем еще посмотреть:

Мероприятия компанииЮридическое лицо— созданная и зарегистрированная в установленном законом порядке организация, которая имеет в собственности, хозяйственном ведении или оперативном управлении обособленное имущество и отвечает по своим обязательствам этим имуществом, может от своего имени приобретать и осуществлять имущественные и личные неимущественные права, нести обязанности, быть истцом и ответчиком в суде. Юридические лица должны иметь самостоятельный баланс или смету..
 за 

СписокСписок— письменный перечень, число, состав; документ, содержащий перечень каких-либо сведений; в переносном смысле— буквальное, точное воспроизведение, копия; рукописная копия древнего памятника письменности. мероприятий за 16 августа 2008 г.
  МероприятиеДата проведенияНаправление
  Акции
 IP-телефонияVoIP (англ.Voice over IP; IP-телефония, произносится «во айпи»)— система связи, обеспечивающая передачу речевого сигнала по сети Интернет или по любым другим IP-сетям. Сигнал по каналу связи передаётся в цифровом виде и, как правило, перед передачей преобразовывается (сжимается) с тем, чтобы удалить избыточность. бесплатно!!C 03 июля по 03 сентября 2008 г.AlcatelAlcatel SA (произносится Алкатель)— ранее французская компания, один из лидеров мирового рынка телекоммуникационного оборудования. Штаб-квартира находилась в Париже.
Компания s-networks совместно с компанией Alcatel-Lucent объявляет новую маркетинговую акцию «IP-телефония бесплатно!!». Партнер компании s-networks, купивший станцию Alcatel-Lucent Omni PCX Office, получает бесплатно IP-телефонию. Срок действия маркетинговой акции «IP-телефония бесплатно!!» с 3 июля по 3 сентября 2008 года.
 Каждый сам кузнец своих кадровC 11 августа по 10 октября 2008 г.Оборудование Cisco

Авторизация
Логин
Пароль
Регистрация >
  Мероприятия « 2008 »   
« август » 
Пн   4111825
Вт   5121926
Ср   6132027
Чт   7142128
Пт  18152229
Сб  29162330
Вс  310172431
Ближайший учебный курс ND 3321 СКС SYSTIMAX пройдёт c 18 по 21 августа 2009 г.
Подать заявку
2009 IT и оборудование для бизнеса, S-NETWORKS. Информационные технологии и Информационное оборудование