Re[5]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 05:35
Оценка:
Здравствуйте, igna, Вы писали:

I>Хорошо, едем дальше. Проверка str_ != 0 в операторах разыменовывания и инкрементирования in release mode не нужна, поскольку сингулярный итератор разыменовывать и инкрементировать запрещено.


да пожалуй вы правы
валидность указателя надо проверять до
Re[6]: cstring_iterator
От: Alexander G Украина  
Дата: 14.07.11 07:57
Оценка: 3 (1)
Здравствуйте, ioni, Вы писали:

I>да пожалуй вы правы

I>валидность указателя надо проверять до

Ещё, operator* () должен возвращать ссылку.
Русский военный корабль идёт ко дну!
Re[7]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 08:06
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, ioni, Вы писали:


I>>да пожалуй вы правы

I>>валидность указателя надо проверять до

AG>Ещё, operator* () должен возвращать ссылку.


тогда уж const
    TChar const& operator* () const
    {
        return *str_;
    }
Re[8]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 08:16
Оценка:
Здравствуйте, ioni, Вы писали:

ну и postincrement до кучи

    const_string_iterator operator++( int )
    {
        const_string_iterator tmp = *this;
        ++*this;
        return tmp;
    }


предварительно окончательный вариант
template < typename TChar >
struct const_string_iterator : public std::iterator< std::forward_iterator_tag, TChar >
{
    TChar const* str_;
    explicit const_string_iterator( TChar const* str ) : str_( *str == 0 ? 0 : str )
    {
    }
    const_string_iterator( ) : str_( 0 )
    {
    }
    TChar const& operator* () const
    {
        return *str_;
    }
    const_string_iterator& operator++()
    {
        ++str_;
        if (*str_ == 0) str_ = 0;
        return *this;
    }
    const_string_iterator operator++( int )
    {
        const_string_iterator tmp = *this;
        ++*this;
        return tmp;
    }
    //////////////////////////////////////////////////////////////////////////
    friend bool operator==(const_string_iterator const& x, const_string_iterator const& y)
    {
        return x.str_ == y.str_;
    }

    friend bool operator!=(const_string_iterator const& x, const_string_iterator const& y)
    {
        return !operator==(x, y);
    }

};

typedef const_string_iterator< char > char_string_iterator;
typedef const_string_iterator< wchar_t > wchar_string_iterator;

//
int main()
{
    const char* str = {"abcd"};
    std::copy( char_string_iterator(str), char_string_iterator(), std::ostream_iterator<char>(std::cout, "\n") );
    //
    const char* str2 = {""};
    size_t len = std::distance( char_string_iterator(str2), char_string_iterator() );
    std::cout << strlen(str2) << " " << len;
    //    
    return 0;
}
Re[9]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 08:22
Оценка:
Здравствуйте, ioni, Вы писали:

I>Здравствуйте, ioni, Вы писали:


вот так postincrement не вылетает

    const_string_iterator operator++( int )
    {
        const_string_iterator tmp = *this;
        if( tmp.str_ ) operator++();
        return tmp;
    }
Re[10]: cstring_iterator
От: Alexander G Украина  
Дата: 14.07.11 08:57
Оценка: 3 (1)
Здравствуйте, ioni, Вы писали:

I>
I>    const_string_iterator operator++( int )
I>    {
I>        const_string_iterator tmp = *this;
I>        if( tmp.str_ ) operator++();
I>        return tmp;
I>    }

I>


Вот поэтому автору и нужен готовый итератор.

Вот на основе почти готового:

#include <boost/iterator/iterator_adaptor.hpp>

class cstring_iterator
    : public boost::iterator_adaptor<
        cstring_iterator,
        char const *,
        boost::use_default,
        boost::forward_traversal_tag>
{
public:
    explicit cstring_iterator(char const * buffer = 0)
        : iterator_adaptor_(buffer)
    {
    }

    void increment()
    {
        if ( *base_reference() )
        {
            ++base_reference();
        }
        else
        {
            base_reference() = 0;
        }
    }

    friend class boost::iterator_core_access;
};


не проверял, но ошибок, скорее всего, меншье будет.
Русский военный корабль идёт ко дну!
Re[11]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 09:16
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>не проверял, но ошибок, скорее всего, меншье будет.

согласен
но вообще то не собирается
Re[12]: cstring_iterator
От: Alexander G Украина  
Дата: 14.07.11 09:37
Оценка:
Здравствуйте, ioni, Вы писали:

I>согласен

I>но вообще то не собирается

Всё собирается. Исправил ошибку, вот:

http://codepad.org/7HVj94bg
Русский военный корабль идёт ко дну!
Re[13]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 10:07
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, ioni, Вы писали:


I>>согласен

I>>но вообще то не собирается

AG>Всё собирается. Исправил ошибку, вот:


AG>http://codepad.org/7HVj94bg

нет ничего совершенного

int main()
{
    const char* str = {"abcd"};
    std::copy( сstring_iterator(str), cstring_iterator(0), std::ostream_iterator<char>(std::cout, "\n") );
    size_t len = std::distance( cstring_iterator(str), cstring_iterator() );
    std::cout << strlen(str) << " " << len; //добавляет лишний символ
    //
    сstring_iterator s = cstring_iterator(str) + 1; //не собираается
    std::copy( s, cstring_iterator(), std::ostream_iterator<char>(std::cout, "\n") );
    //
    const char* str2 = {""};
    std::copy( cstring_iterator(str2), cstring_iterator(0), std::ostream_iterator<char>(std::cout, "\n") );
    len = std::distance( cstring_iterator(str2), cstring_iterator() );
    std::cout << strlen(str2) << " " << len;//добавляет лишний символ
    //    
    return 0;
}
Re[14]: cstring_iterator
От: jazzer Россия Skype: enerjazzer
Дата: 14.07.11 10:29
Оценка:
Здравствуйте, ioni, Вы писали:

I>
I>    сstring_iterator s = cstring_iterator(str) + 1; //не собираается
I>


и не должно — это же forward iterator, он "+" и не должен понимать, вроде как.
std::advance работает?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[15]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 10:33
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, ioni, Вы писали:


I>>
I>>    сstring_iterator s = cstring_iterator(str) + 1; //не собираается
I>>


J>и не должно — это же forward iterator, он "+" и не должен понимать, вроде как.

J>std::advance работает?

это компилируется
std::advance( cstring_iterator(str), 1 );
Re[15]: cstring_iterator
От: ioni Россия  
Дата: 14.07.11 10:46
Оценка: 3 (1) :))
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, ioni, Вы писали:


I>>
I>>    сstring_iterator s = cstring_iterator(str) + 1; //не собираается
I>>


J>и не должно — это же forward iterator, он "+" и не должен понимать, вроде как.

вы правы если поменять forward на random_access то вообще все падает
Re[10]: cstring_iterator
От: igna Россия  
Дата: 14.07.11 14:48
Оценка:
Здравствуйте, ioni, Вы писали:

I>вот так postincrement не вылетает


I>        if( tmp.str_ ) operator++();


Эту проверка опять же лишняя in release mode, поскольку если _str == 0, то итератор сингулярный, а сингулярный итератор инкрементировать нельзя.
Re[13]: cstring_iterator
От: igna Россия  
Дата: 14.07.11 17:10
Оценка: 1 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Всё собирается. Исправил ошибку, вот:


AG>http://codepad.org/7HVj94bg


Там очевидно тоже копируется '\0' в случае пустой строки.
Re[14]: cstring_iterator
От: Alexander G Украина  
Дата: 14.07.11 21:32
Оценка: 1 (1)
Здравствуйте, igna, Вы писали:

I>Там очевидно тоже копируется '\0' в случае пустой строки.


А, ну да. Может переопределить не increment, а equal, как я ранее предложил:


class cstring_iterator
    : public boost::iterator_adaptor<
        cstring_iterator,
        char const *,
        boost::use_default,
        boost::forward_traversal_tag>
{
public:
    explicit cstring_iterator(char const * buffer = 0)
        : iterator_adaptor_(buffer)
    {
    }
 
    bool equal(cstring_iterator const & other) const
    {
        if ( other.base() == NULL )
        {
           return ( *this->base() == '\0' );
        }
        else if ( this->base() == NULL )
        {
           return ( *other.base() == '\0' );
        }
        else
        {
           return ( other.base() == this->base() );
        }
    }
 
    friend class boost::iterator_core_access;
};
Русский военный корабль идёт ко дну!
Re[15]: cstring_iterator
От: igna Россия  
Дата: 15.07.11 05:56
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>А, ну да. Может переопределить не increment, а equal, как я ранее предложил:


AG>    bool equal(cstring_iterator const & other) const
AG>    {
AG>        if ( other.base() == NULL )
AG>        {
AG>           return ( *this->base() == '\0' );
AG>        }
AG>        else if ( this->base() == NULL )
AG>        {
AG>           return ( *other.base() == '\0' );
AG>        }
AG>        else
AG>        {
AG>           return ( other.base() == this->base() );
AG>        }
AG>    }


Логически это именно то, что надо, но может оказаться неоптимальным в некоторых случаях; например, если сравниваются два несингулярных итератора. Если достаточен forward iterator, лучше все же обнулять указатель сразу как только он устанавливается на конец строки. (Мест таких два: в конструкторе и в операторе инкрементирования, поэтому проверку и обнуление имеет смысл вынести в отдельную функцию.)

А вот если нужен random access iterator, то может быть и так, как ты предложил, хотя я бы подумал еще, не лучше ли сохранять указатель на последний символ/знак строки перед обнулением указателя, чтобы оператор декрементирования мог восстановить его. Правда тогда в итераторе потребуется еще одно поле.

В любом случае библиотека общего назначения должна бы иметь оба эти итератора для строки заканчивающейся нулем: один как forward iterator, второй как random access iterator.
Re[16]: cstring_iterator
От: Alexander G Украина  
Дата: 15.07.11 06:55
Оценка:
Здравствуйте, igna, Вы писали:

I>Здравствуйте, Alexander G, Вы писали:


AG>>А, ну да. Может переопределить не increment, а equal, как я ранее предложил:


I>
AG>>    bool equal(cstring_iterator const & other) const
AG>>    {
AG>>        if ( other.base() == NULL )
AG>>        {
AG>>           return ( *this->base() == '\0' );
AG>>        }
AG>>        else if ( this->base() == NULL )
AG>>        {
AG>>           return ( *other.base() == '\0' );
AG>>        }
AG>>        else
AG>>        {
AG>>           return ( other.base() == this->base() );
AG>>        }
AG>>    }
I>


I>Логически это именно то, что надо, но может оказаться неоптимальным в некоторых случаях; например, если сравниваются два несингулярных итератора.


Я думаю, цикл с диапазоном из двух несингулярных итераторов в случае такого equal оптимальнее.
В случае проверки в increment будет разыменование. В случае проверки в equal выполнится последняя ветка, которая ничего не разыменует.

I>А вот если нужен random access iterator, то может быть и так, как ты предложил, хотя я бы подумал еще, не лучше ли сохранять указатель на последний символ/знак строки перед обнулением указателя, чтобы оператор декрементирования мог восстановить его. Правда тогда в итераторе потребуется еще одно поле.


Хорошего random access iterator из этой идеи не получится. Для random access iterator требуется операция distance, она будет за линейное время при наличии сингулярного итератора. Даже хорошего bidirectional не получится — past-the-end должен быть декрементируемым, это не сработает для cstring_iterator, изначально сконструированного сингулярным.

I>В любом случае библиотека общего назначения должна бы иметь оба эти итератора для строки заканчивающейся нулем: один как forward iterator, второй как random access iterator.


Должна библиотека общего назначения включать такие микрооптимизации?
Русский военный корабль идёт ко дну!
Re[17]: cstring_iterator
От: igna Россия  
Дата: 15.07.11 08:58
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>В случае проверки в increment будет разыменование.


В типичном случае это разыменование будет соптимизировано, поскольку где-то неподалеку будет находиться еще одно разыменование от оператора разыменования. А вот сравнение с нулем в типичном случае сравнения несингулярных итераторов соптимизировать будет нельзя.

AG>Хорошего random access iterator из этой идеи не получится. Для random access iterator требуется операция distance, она будет за линейное время при наличии сингулярного итератора.


Это не так, ты же знаешь, что символы строки хранятся непрерывно, значит смело можешь вычитать. Даже проверка на 0 in release mode не нужна, поскольку сингулярный итератор в качестве аргумента distance использовать нельзя.
Re[18]: cstring_iterator
От: Alexander G Украина  
Дата: 15.07.11 11:16
Оценка: 9 (1)
Здравствуйте, igna, Вы писали:

I>В типичном случае это разыменование будет соптимизировано, поскольку где-то неподалеку будет находиться еще одно разыменование от оператора разыменования. А вот сравнение с нулем в типичном случае сравнения несингулярных итераторов соптимизировать будет нельзя.


Если считать сравнения, то сравнение с нулём всё равно будет, в данном случае с нулевым символом. Нужно смотреть результат компиляции, чтобы сказать точно, что быдет лучше.


AG>>Хорошего random access iterator из этой идеи не получится. Для random access iterator требуется операция distance, она будет за линейное время при наличии сингулярного итератора.


I>Это не так, ты же знаешь, что символы строки хранятся непрерывно, значит смело можешь вычитать. Даже проверка на 0 in release mode не нужна, поскольку сингулярный итератор в качестве аргумента distance использовать нельзя.


http://codepad.org/7zE1H2e8
Русский военный корабль идёт ко дну!
Re[19]: Результат компиляции
От: igna Россия  
Дата: 15.07.11 16:15
Оценка: 1 (1)
Здравствуйте, Alexander G, Вы писали:

AG>Если считать сравнения, то сравнение с нулём всё равно будет, в данном случае с нулевым символом. Нужно смотреть результат компиляции, чтобы сказать точно, что быдет лучше.


Смотрим результат компиляции:

increment
$LL45@main:
    movsx    ecx, cl
    inc    edx
    add    eax, ecx
    mov    cl, BYTE PTR [edx]
    test    cl, cl
    jne    SHORT $LL45@main


equal
$LL41@main:
    mov    cl, BYTE PTR [edx]
    test    cl, cl
    sete    bl
    test    bl, bl
    jne    SHORT $LN76@main
    movsx    ecx, cl
    add    eax, ecx
    inc    edx
    jmp    SHORT $LL41@main
$LN76@main:
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.