Что такое traits?
Страница 4.


Но вот незадача: этот код не компилируется. Дело в том, что pos_type должен уметь конструироваться из нескольких заранее определенных типов(как показало исследование, 2). Базовые типы этого делать не умеют, так что придется написать свой собственный класс. Я не буду заострять внимание на этом классе, так как статья немного не на эту тему. Я просто приведу реализацию этого класса, а если у вас будут какие-то вопросы, то писать либо здесь, либо в PM. Итак, вот код:

// пространство имен, в которое заносятся детали реализации
namespace detail {
 template <typename num_type, typename state_type = std::mbstate_t>
 class pos_type_t {
     typedef pos_type_t<num_type, state_type> my_type;

     num_type    m_pos;
     state_type  m_state;

     static state_type initial_state;

    public:
    // конструкторы
     pos_type_t(std::streampos off) : m_pos(off), m_state(initial_state) {}
        pos_type_t(num_type off = 0) : m_pos(off), m_state(initial_state) {}
        pos_type_t(state_type state, num_type pos) : m_pos(pos), m_state(state) {}

        // получение состояния потока
        state_type state() const {
            return(m_state);
        }

        // установка состояния потока
        void state(state_type st) {
            m_state = st;
        }

        // получение позиции
        num_type seekpos() const    {
        return(m_pos);
        }

        // оператор преобразования
        operator num_type() const {
        return(m_pos);
        }

        // далее идут операторы, которые осуществляют арифметические операции

        num_type operator- (const my_type& rhs) const {
    return(static_cast<num_type>(*this) - static_cast<num_type>(rhs));
        }

        my_type& operator+= (num_type pos) {
            m_pos += pos;
            return(*this);
        }

        my_type& operator-= (num_type pos) {
            m_pos -= pos;
            return(*this);
        }

        my_type operator+ (num_type pos) const {
            my_type tmp(*this);
            return(tmp += pos);
        }

        my_type operator- (num_type pos) const {
            my_type tmp(*this);
            return(tmp -= pos);
        }

        // операторы сравнения

        bool operator== (const my_type& rhs) const {
    return(static_cast<num_type>(*this) == static_cast<num_type>(rhs));
        }

        bool operator!= (const my_type& rhs) const {
    return(!(*this == rhs));
        }
 };
//---------------------------------------------------
 // статическая константа, которая обозначает начальное состояние
 template <typename num_type, typename state_type>
 state_type pos_type_t<num_type, state_type>::initial_state;
}
//---------------------------------------------------
// наконец-то наш класс свойств:
template <typename char_t, typename long_pos_t>
struct long_pointer_traits : public std::char_traits<char_t> {
 // определение pos_type через наш только что написанный класс
 typedef detail::pos_type_t<long_pos_t> pos_type;

 // определение off_type через тип, переданный во 2 аргументе шаблона
 typedef long_pos_t off_type;
};
//---------------------------------------------------
// вводим тип "длинного" файла
typedef std::basic_ifstream<char, long_pointer_traits<char, __int64> > long_ifstream;

// используем long_ifstream для получения размера файла

ОК, теперь все компилируется и работает. Но кроме получения позиции в файле, нам обычно надо работать еще с этими файлами(читать, писать). И, конечно, нам приходится работать со строками. Тогда если мы попытаемся считать строку из файла таким образом:
long_ifstream infile(strFileName, std::ios::binary);
std::string res;
std::getline(infile, res);

То мы получим ошибку компиляции. Проблема в том, что std::string - это "всего лишь" typedef от std::basic_string. Этот класс принимает 2 параметра шаблона: первый - тип для представления символа, а второй(как вы уже, наверное, догадались) - traits. Так вот, для корректной работы нам надо определить и свой тип строки:

// "длинные" типы:
typedef std::basic_ifstream<char, long_pointer_traits<char, __int64> >  long_ifstream;
typedef std::basic_string<char, long_pointer_traits<char, __int64> >    long_string;

long_ifstream infile(strFileName, std::ios::binary);
long_string res;
std::getline(infile, res);

Теперь все работает прекрасно. Таким образом, для правильного взаимодействия компонентов стандартной библиотеки нам придется определять нужные типы и работать с ними. К сожалению, на данный момент я не знаю способа, как можно было бы создать нужный тип для стандартных потоков ввода/вывода(cin, cout, cerr, clog). Так что чтобы вывести такую "длинную" строку на экран, надо будет написать свой оператор вывода такой строки. Другого решения мне неизвестно(если кто-то знает - поделитесь, буду признателен).
Также хочу сказать несколько слов о совместимости и переносимости: приведенный мной код по определению размера большого файла был проверен на компиляторах VC7.1 и Intel C++ 8.0. Использовалась стандартная библиотека, которая идет по умолчанию с VC. При работе с ней замечено никаких ошибок не было. Проверялся код и с использованием STLPort версий 4.6.2 и 5.0. Компилировался он без проблем, но работал неправильно. Надеюсь, в дальнейших версиях STLPort'а это будет исправлено и работать будет корректно, так как данный код соответствует стандарту.
 
« Предыдущая статья   Следующая статья »