Re: ненависть к итераторам
От: so5team https://stiffstream.com
Дата: 26.12.20 12:50
Оценка:
Здравствуйте, Videoman

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

Сложилось впечатление, что у вас были обычные перегрузки для нескольких типов. Которые неявно подразумевали, что [begin, end) -- это непрерывный блок данных. Теперь вы захотели сделать свои шаблонные функции, которые бы принимали в качестве begin и end итераторы, дабы можно было работать с произвольными последовательностями, необязательно непрерывными.

Если так, то сходу напрашивается что-то такое:
namespace impl
{

template<typename Basic_Type>
struct calc_something_impl;

template<>
struct calc_something_impl<char> {
  template<typename First_It, typename Last_It>
  static void calc(First_It begin, Last_It end) {...}
};

template<>
struct calc_something_impl<char16_t> {
  template<typename First_It, typename Last_It>
  static void calc(First_It begin, Last_It end) {...}
};

... // Тоже самое и для других нужных вам типов.
} /* namespace impl */

template<typename First_It, typename Last_It>
void calc_something(First_It begin, Last_It end) {
  using value_type = decltype(*begin);
  impl::calc_something_impl<value_type>::calc(begin, end);
}


В calc_something вы так же можете поместить нужный вам код для разбирательства с тем, что на самом деле представляет из себя wchar_t.
Re[5]: ненависть к итераторам
От: Voivoid Россия  
Дата: 26.12.20 12:52
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Да придирки... просто насколько я понял std::iterator_traits не отличает константные итераторы от не константных. Как с std::iterator_traits сделать что-бы метод принимал итераторы/указатели только не константные, иначе говоря, что бы код выше не компилировался, а вот такой компилировался?


Тогда что-нибудь типа такого можно добавить:

template <typename Iter>
constexpr bool is_const_iter_v = std::is_const< std::remove_reference_t< std::iterator_traits< Iter >::reference > >::value;


Итоговый пример: https://ideone.com/UtLFvi
Re[5]: ненависть к итераторам
От: rg45 СССР  
Дата: 26.12.20 14:31
Оценка: 4 (1)
Здравствуйте, Videoman, Вы писали:


V>Да придирки... просто насколько я понял std::iterator_traits не отличает константные итераторы от не константных. Как с std::iterator_traits сделать что-бы метод принимал итераторы/указатели только не константные, иначе говоря, что бы код выше не компилировался, а вот такой компилировался?


Константный и неконстантный итераторы имеют одинаковый value_type, но разные reference. Так что все в твоих руках. Вот так, например:

template <typename It>
using is_nonconst_iterator = std::is_same<
  typename std::iterator_traits<It>::reference, 
  typename std::iterator_traits<It>::value_type&>;


template<typename It, std::enable_if_t<is_nonconst_iterator<It>::value, int> = 0>
constexpr uint_t CalcSomething(It beg, It end);
--
Отредактировано 26.12.2020 14:48 rg45 . Предыдущая версия .
Re: ненависть к итераторам
От: sergii.p  
Дата: 27.12.20 11:31
Оценка:
Здравствуйте, Videoman, Вы писали:

V>
V>    constexpr uint_t CalcSomething(const char* beg, const char* end) // ...
V>    constexpr uint_t CalcSomething(const char16_t* beg, const char16_t* end) // ...
V>    constexpr uint_t CalcSomething(const char32_t* beg, const char32_t* end) // ...
V>    constexpr uint_t CalcSomething(const wchar_t* beg, const wchar_t* end) // ...
V>


я обычно так и пишу. Но внутри делегирую вызов обычной шаблонной функции без ограничений (типа CalcSmthImpl). Получается извне функция ограничена по типам. А функция CalcSmthImpl обычно спрятана в cpp файле и её никто не видит. Можно такое же делать со специализацией, но ошибка при этом получается менее красивая
Re[2]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 09:45
Оценка:
Здравствуйте, so5team, Вы писали:

S>Не понятна сама задача, которую вы пытаетесь решить. Вместо того, чтобы ее описать так, чтобы она была понятна сторонним людям, вы выкатили кучу кода, который иллюстрирует лишь ту попытку, которая вам не нравится. Что отвлекает от поиска решения исходной задачи, провоцируя поиск исправления вашей попытки. Может быть, если вы сформулируете то, что вам хочется, то вам быстрее подскажут в какую сторону двигаться.


Почему вы думаете что я не в состоянии решить возникающие передо мной задачи, кроме как обращаясь за советом на RSDN и делая copy-paste. Задача специально описана несколько абстрактно, т.к. меня интересовал общий подход на уровне С++17. Я просто хотел удостовериться что нигде не переусложняю код. Тем не менее, несколько человек не только поняли о чем речь, но и помогли без нравоучений и дали очень полезные и дельные советы.

S>Если так, то сходу напрашивается что-то такое:

S>
S>namespace impl
S>{

S>template<typename Basic_Type>
S>struct calc_something_impl;

S>template<>
S>struct calc_something_impl<char> {
S>  template<typename First_It, typename Last_It>
S>  static void calc(First_It begin, Last_It end) {...}
S>};

S>template<>
S>struct calc_something_impl<char16_t> {
S>  template<typename First_It, typename Last_It>
S>  static void calc(First_It begin, Last_It end) {...}
S>};

S>... // Тоже самое и для других нужных вам типов.
S>} /* namespace impl */

S>template<typename First_It, typename Last_It>
S>void calc_something(First_It begin, Last_It end) {
S>  using value_type = decltype(*begin);
S>  impl::calc_something_impl<value_type>::calc(begin, end);
S>}
S>


S>В calc_something вы так же можете поместить нужный вам код для разбирательства с тем, что на самом деле представляет из себя wchar_t.


Нет, так не пойдет:
1. У вас все ошибки будут происходить уже после разрешения перегрузки void calc_something(First_It begin, Last_It end) в ее потрохах. В исходных данных у меня было просто четыре функции, которые просто игнорируются если типы не подходят.
2. У вас не соблюдается const-корректность, функции не учитывают что аргументы должны обладать семантикой константных указателей.
3. Вы почему-то игнорируете то, что методы constexpr и абсолютно не помогли возникающей в связи с этим проблемой с конвертацией итераторов wchar_t в соответствующие типы.

P.S. На самом деле ответы на интересующие вопросы я получил. Еще раз убедился что современный С++ все еще не совершенен и ему есть куда стремиться.
Если кому интересно в итоге в общем виде решение у меня получилось такое:
    // Non-const iterator-like type check

    template<typename It, typename... Value> 
    struct is_iterator_of
    {
        using ItValue = typename std::iterator_traits<It>::value_type;
        using ItReference = typename std::iterator_traits<It>::reference;

        template<typename It>
        static constexpr bool is_non_const_v = std::is_same_v<
            std::remove_reference_t<ItReference>
            , std::remove_const_t<std::remove_reference_t<ItReference>>>;

        static constexpr bool value = std::disjunction_v<std::is_same<ItValue, Value>...> && is_non_const_v<It>;
    };

    template<typename It, typename... Value>
    constexpr bool is_iterator_of_v = is_iterator_of<It, Value...>::value;

    // Const iterator-like type check

    template<typename It, typename... Value>
    struct is_const_iterator_of
    {
        using ItValue = typename std::iterator_traits<It>::value_type;

        static constexpr bool value = std::disjunction_v<std::is_same<ItValue, Value>...>;
    };

    template<typename It, typename... Value>
    constexpr bool is_const_iterator_of_v = is_const_iterator_of<It, Value...>::value;



    ////////////////////////////////////////////////////////////////////////////////////////////////
    // Standalone methods
    ////////////////////////////////////////////////////////////////////////////////////////////////

    template<typename It, std::enable_if_t<is_const_iterator_of_v<It, char>, int> = 0>
    constexpr It CalcSomething(It str, It last) noexcept;
    template<typename It, std::enable_if_t<is_const_iterator_of_v<It, char16_t>
        || (is_const_iterator_of_v<It, wchar_t> && sizeof(wchar_t) == sizeof(char16_t)), int> = 0>
    constexpr It CalcSomething(It str, It last) noexcept;
    template<typename It, std::enable_if_t<is_const_iterator_of_v<It, char32_t>
        || (is_const_iterator_of_v<It, wchar_t> && sizeof(wchar_t) == sizeof(char32_t)), int> = 0>
    constexpr It CalcSomething(It str, It last) noexcept;

В результате перегрузки просто игнорятся если типы не подходят, константность соблюдается, все работает на этапе компиляции без всяких преобразований типов.
Re: ненависть к итераторам
От: B0FEE664  
Дата: 28.12.20 10:19
Оценка:
Здравствуйте, Videoman, Вы писали:

V>За что я "люблю" эту концепцию STL так это за то, что наступаешь на грабли в самых казалось бы простых вещах!

V>Захотел написать обобщенный код, было:
V>
V>    constexpr uint_t CalcSomething(const char* beg, const char* end) // ...
V>    constexpr uint_t CalcSomething(const char16_t* beg, const char16_t* end) // ...
V>    constexpr uint_t CalcSomething(const char32_t* beg, const char32_t* end) // ...
V>    constexpr uint_t CalcSomething(const wchar_t* beg, const wchar_t* end) // ...
V>

V>Все отлично работает, был простой код. Не совсем гибко, а давайте еще обобщим и добавим что бы работало с любыми итераторами.
V>Проблема первая, как перегрузить итераторы только для перечисленных типов?

Смею предположить, что концепция STL и ваш подход — это в некотором смысле перпендикулярные подходы. В парадигме STL, как я её понимаю, вместо ваших CalcSomething(..) следует вызывать
std::for_each(itBegin, itEnd, fnFunctor) и передавать третьим параметром специализированный функтор. Смысл в том, чтобы отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге. Таким образом вам нужно перегружать не итераторы, а функторы. При этом сам функтор иногда (не всегда) имеет смысл разделить на две сущности: замыкание хранящие результат вычисления на каждом шаге и функцию подсчёта следующего значения. В результате должно получится нечто вроде:

uint_t result{};
std::for_each(itBegin, itEnd, [&result](char byte){ result = Calc(result, byte); });


(Начиная с С++11 std::for_each можно заменить на Range-based for loop, если проход совершается по всему контейнеру.)

Таким образом задача "перегрузить итераторы только для перечисленных типов" сводится к задаче перегрузке функции Calc(PreviousResult, NewValue).
И каждый день — без права на ошибку...
Re[2]: ненависть к итераторам
От: watchmaker  
Дата: 28.12.20 10:55
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Смею предположить, что концепция STL и ваш подход — это в некотором смысле перпендикулярные подходы. В парадигме STL, как я её понимаю, вместо ваших CalcSomething(..) следует вызывать

BFE>std::for_each(itBegin, itEnd, fnFunctor) и передавать третьим параметром специализированный функтор. Смысл в том, чтобы отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге. Таким образом вам нужно перегружать не итераторы, а функторы.

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

Можно сказать, что как раз неработоспособность такого подхода — это причина почему в C++ (да и в других языках) есть отдельные функции std::sort, std::nth_element и std::binary_search, но нет std::for_each(itBegin, itEnd, std::sort_functor{}), for_each(…, std::nth_element_functor{n}) или for_each(…, std::binary_search_functor{n}).



То есть, конечно, можно сделать сортировку или тот же nth_element через вызов вида std::for_each(itBegin, itEnd, some_algo_functor{}), если в функторе завести std::vector<value_type*> и на каждой итерации for_each делать push_back в него. И после накопления всех данных сделать уже реальную работу. Но это будет совсем не бесплатно. Да и ужасно плохо обобщается на случай только input_iterator (где только прямой проход), так как элемент может перестать существовать после it++ (то есть std::for_each ошибку не обнаружит, так как для него требований input_iterator достаточно).
Re[2]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 10:55
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Смею предположить, что концепция STL и ваш подход — это в некотором смысле перпендикулярные подходы. В парадигме STL, как я её понимаю, вместо ваших CalcSomething(..) следует вызывать

BFE>std::for_each(itBegin, itEnd, fnFunctor) и передавать третьим параметром специализированный функтор. Смысл в том, чтобы отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге. Таким образом вам нужно перегружать не итераторы, а функторы. При этом сам функтор иногда (не всегда) имеет смысл разделить на две сущности: замыкание хранящие результат вычисления на каждом шаге и функцию подсчёта следующего значения. В результате должно получится нечто вроде:

BFE>
BFE>uint_t result{};
BFE>std::for_each(itBegin, itEnd, [&result](char byte){ result = Calc(result, byte); });
BFE>


BFE>(Начиная с С++11 std::for_each можно заменить на Range-based for loop, если проход совершается по всему контейнеру.)


BFE>Таким образом задача "перегрузить итераторы только для перечисленных типов" сводится к задаче перегрузке функции Calc(PreviousResult, NewValue).


Ох. Мысль интересная, но на сколько данный подход правильные и универсальный мне сложно судить. Давайте немного усложним код, допусти так:
constexpr const char* CalcSomething(const char* beg, const char* end) // ...
constexpr const char16_t* CalcSomething(const char16_t* beg, const char16_t* end) // ...
constexpr const char32_t* CalcSomething(const char32_t* beg, const char32_t* end) // ...
constexpr const wchar_t* CalcSomething(const wchar_t* beg, const wchar_t* end) // ...

т.е. у нас на выходе итератор зависит от алгоритма внутри метода. Что на это скажет STL и на сколько будет более читаемы код на выходе?

P.S. Мне не нравится как многие вещи в STL сделаны, просто приходится с этим жить. Вообще если посмотреть со стороны, то мой подход тоже вполне вписывается в функциональный стиль — функция "чистая", аргументы константные, принимает все что подходит. По сигнатуре не обязательно должно быть что там какой-то цикл от beg до end.
Re[3]: ненависть к итераторам
От: so5team https://stiffstream.com
Дата: 28.12.20 11:00
Оценка:
Здравствуйте, Videoman, Вы писали:

S>>Не понятна сама задача, которую вы пытаетесь решить. Вместо того, чтобы ее описать так, чтобы она была понятна сторонним людям, вы выкатили кучу кода, который иллюстрирует лишь ту попытку, которая вам не нравится. Что отвлекает от поиска решения исходной задачи, провоцируя поиск исправления вашей попытки. Может быть, если вы сформулируете то, что вам хочется, то вам быстрее подскажут в какую сторону двигаться.


V>Почему вы думаете что я не в состоянии решить возникающие передо мной задачи, кроме как обращаясь за советом на RSDN и делая copy-paste.


Вот почему вы решили что я думаю именно так.

Вы задали вопрос на форуме. На меня ваш вопрос произвел впечатление, что вы хотели решить какую-то задачу и получили код, который вам не нравится. И спрашиваете как улучшить код. При этом стороннему читателю (т.е. мне конкретно) не понятно что за задачу вы решаете. О чем я вам и сказал. Мол, если бы вы озвучили именно задачу (пусть и абстрактно, но подробнее), то вам могли бы посоветовать совсем другие способы ее решения.

V>Я просто хотел удостовериться что нигде не переусложняю код.


Как по мне, так вы как раз простого кода и не получили.

V>Тем не менее, несколько человек не только поняли о чем речь, но и помогли без нравоучений и дали очень полезные и дельные советы.


Извините, что оказался не из их числа.

V>Нет, так не пойдет:


Нет проблем, хозяин барин.

V>1. У вас все ошибки будут происходить уже после разрешения перегрузки void calc_something(First_It begin, Last_It end) в ее потрохах. В исходных данных у меня было просто четыре функции, которые просто игнорируются если типы не подходят.


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

V>2. У вас не соблюдается const-корректность, функции не учитывают что аргументы должны обладать семантикой константных указателей.


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

V>3. Вы почему-то игнорируете то, что методы constexpr


А у вас не хватило фантазии, чтобы в предложенной мной схеме расставить constexpr по вкусу? Ну OK.

V>и абсолютно не помогли возникающей в связи с этим проблемой с конвертацией итераторов wchar_t в соответствующие типы.


А с этим есть проблема? Какая, если не секрет. Вопрос без сарказма из иронии.

V>Если кому интересно в итоге в общем виде решение у меня получилось такое:


Остается пожелать удачи тому, кто в этих портянках затем будет разбираться. И остается надеятся, что в С++20 от этого пердолинга можно будет избавится за счет концептов.
Re[4]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 11:14
Оценка:
Здравствуйте, so5team, Вы писали:

S>Как по мне, так вы как раз простого кода и не получили.


Во всяком случае я получил ответ на свой вопрос.

S>Не факт, что это лучше. Потому что зачастую разбираться с тем, почему компилятор не смог выбрать конкретную перегрузку, гораздо сложнее, чем увидеть уведомление об ошибке о том, что реализации для конкретного типа нет. Говорю как человек, у которого в сообщениях об ошибках компилятора имена типов занимают по несколько экранов.


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

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


Извиняюсь, следующий раз буду описывать подробнее.

V>>3. Вы почему-то игнорируете то, что методы constexpr


S>А у вас не хватило фантазии, чтобы в предложенной мной схеме расставить constexpr по вкусу? Ну OK.


Да не в этом дело.

V>>и абсолютно не помогли возникающей в связи с этим проблемой с конвертацией итераторов wchar_t в соответствующие типы.


S>А с этим есть проблема? Какая, если не секрет. Вопрос без сарказма из иронии.


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

V>>Если кому интересно в итоге в общем виде решение у меня получилось такое:


S>Остается пожелать удачи тому, кто в этих портянках затем будет разбираться. И остается надеятся, что в С++20 от этого пердолинга можно будет избавится за счет концептов.


А что делать. Да и не переживайте вы так, никто особо разбираться не будет. Это библиотечный код, остальные будут использовать и радоваться что получают вменяемые ошибки, а не простыни невразумительных ошибок от компилятора.
Re[5]: ненависть к итераторам
От: so5team https://stiffstream.com
Дата: 28.12.20 11:29
Оценка:
Здравствуйте, Videoman, Вы писали:

S>>Не факт, что это лучше. Потому что зачастую разбираться с тем, почему компилятор не смог выбрать конкретную перегрузку, гораздо сложнее, чем увидеть уведомление об ошибке о том, что реализации для конкретного типа нет. Говорю как человек, у которого в сообщениях об ошибках компилятора имена типов занимают по несколько экранов.


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


Если у вас calc_something может работать не только с разными char-ами, но и с какими-нибудь int-ами или float-ами, но при этом она сохраняет и свое имя, и свой набор аргументов, то какая проблема поддерживать новые типы в impl::some_calc_something?

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


V>Извиняюсь, следующий раз буду описывать подробнее.


Я вот так и не понял, если вы хотите, чтобы допускался такой код:
char * p = ...;
calc_something(p, p+3);

то почему должно быть запрещено вот такое?
char * p = ...;
const char * e = p+3;
calc_something(p, e);



V>>>и абсолютно не помогли возникающей в связи с этим проблемой с конвертацией итераторов wchar_t в соответствующие типы.


S>>А с этим есть проблема? Какая, если не секрет. Вопрос без сарказма из иронии.


V>Проблема в том, что бы с одной стороны не дублировать код, а с другой стороны не нарваться на преобразование итераторов в указатели или указателей в указатели другого типа, которое в constexpr методах запрещено.


Так что это за проблемы? wchar_t -- это не отдельный тип, а просто typedef для чего-то и компилятор не разделяет unsigned short и wchar_t? Или что-то другое?

V>Это библиотечный код, остальные будут использовать и радоваться что получают вменяемые ошибки, а не простыни невразумительных ошибок от компилятора.


Как бы намек на то, что когда компилятор говорит, что не может выбрать одну из N перегрузок, в каждой из которых стоит забористый enable_if_t, то пользователю от этого лучше не становится.
Re: ненависть к итераторам
От: XOOIOOX  
Дата: 28.12.20 16:15
Оценка:
Здравствуйте, Videoman, Вы писали:

V>
V>    constexpr uint_t CalcSomething(const char* beg, const char* end) // ...
V>    constexpr uint_t CalcSomething(const char16_t* beg, const char16_t* end) // ...
V>    constexpr uint_t CalcSomething(const char32_t* beg, const char32_t* end) // ...
V>    constexpr uint_t CalcSomething(const wchar_t* beg, const wchar_t* end) // ...
V>


Я дико извиняюсь, но это же не STL'ные итераторы. Может, если использовать стандартные контейнеры и их итераторы, не придется городить огород?
Re[2]: ненависть к итераторам
От: watchmaker  
Дата: 28.12.20 16:35
Оценка: +2 :)
Здравствуйте, XOOIOOX, Вы писали:

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


V>>
V>>    constexpr uint_t CalcSomething(const char* beg, const char* end) // ...
V>>    constexpr uint_t CalcSomething(const char16_t* beg, const char16_t* end) // ...
V>>    constexpr uint_t CalcSomething(const char32_t* beg, const char32_t* end) // ...
V>>    constexpr uint_t CalcSomething(const wchar_t* beg, const wchar_t* end) // ...
V>>


XOO>Я дико извиняюсь, но это же не STL'ные итераторы.


Вот как раз STL про всех них говорит, что это самые настоящие итераторы, со всеми причитающимися им свойствами, например:
using test = std::iterator_traits<const char*>::value_type;

static_assert(
    std::is_same_v<
        std::iterator_traits<const char*>::iterator_category,
        std::random_access_iterator_tag
    >
);
Отредактировано 28.12.2020 17:32 watchmaker . Предыдущая версия . Еще …
Отредактировано 28.12.2020 16:37 watchmaker . Предыдущая версия .
Re[3]: ненависть к итераторам
От: B0FEE664  
Дата: 28.12.20 16:45
Оценка:
Здравствуйте, watchmaker, Вы писали:

BFE>>Смею предположить, что концепция STL и ваш подход — это в некотором смысле перпендикулярные подходы. В парадигме STL, как я её понимаю, вместо ваших CalcSomething(..) следует вызывать

BFE>>std::for_each(itBegin, itEnd, fnFunctor) и передавать третьим параметром специализированный функтор. Смысл в том, чтобы отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге. Таким образом вам нужно перегружать не итераторы, а функторы.

W>Во-первых, мягко говоря, тут спорное утверждение про парадигму stl Откуда оно взялось?

Вот так я понимаю парадигму stl, о чём и написано выше.

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

Что ? Сортировка не работает для суммирования? Вроде же ясно написал "отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге". Вот у вас есть линейный проход: for_each(..), а есть нелинейный проход: std::binary_search. Есть операции изменения последовательности std::rotate и sort и пр., а есть операции изменения значений std::transform. Почти в каждом случае есть разделение операций над самой последовательностью и операций над данными последовательности. Почти, потому что есть такие функции как find и remove, которые используют прописанные операции сравнения прямо в алгоритме, но даже для них есть возможность переопределить эту операцию для объектов или же использовать вариант с суффиксом _if. Так что я совсем не понимаю ваших претензий.
И каждый день — без права на ошибку...
Re[6]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 17:21
Оценка:
S>Если у вас calc_something может работать не только с разными char-ами, но и с какими-нибудь int-ами или float-ами, но при этом она сохраняет и свое имя, и свой набор аргументов, то какая проблема поддерживать новые типы в impl::some_calc_something?

У меня такая специфика. Я пишу библиотеки, в том числе, и как их будут использовать мне не известно. На самом деле функций куча и они вызывают друг друга. Я предоставляю базовый функционал, если человек хочет "оптимизировать", например свою реализацию сделать, только отдельную функцию, то он только ее и предоставляет, причем в своем namespace. В вашем подходе, компиляция проваливается, по сути, внутрь библиотеки, и там куча всяких вызовов может быть, а уже там не срабатывает почему-то перегрузка. Внутри библиотеки уже может быть не так просто подставить свою реализацию. Короче — так намного гибче и расширяемее.

S>Я вот так и не понял, если вы хотите, чтобы допускался такой код:

S>
S>char * p = ...;
S>calc_something(p, p+3);
S>

S>то почему должно быть запрещено вот такое?
S>
S>char * p = ...;
S>const char * e = p+3;
S>calc_something(p, e);
S>


А кто сказал что такое запрещено. Запрещено такое:
const char* p = ...;
const char* e = p + 3;
modify_something(p, e);


S>Так что это за проблемы? wchar_t -- это не отдельный тип, а просто typedef для чего-то и компилятор не разделяет unsigned short и wchar_t? Или что-то другое?

wchar_t — это build-in тип, но в зависимости от платформы я хочу что бы он вызывал либо специализацию для char16_t, либо char32_t, что бы не повторять весь их функционал, но мало того что в constexpr методах запрещено преобразование одних указателей в другие, так еще и преобразование итераторов в общем виде не реализуемо в принципе. Вот мне и подсказали как выйти из данной ситуации.

S>Как бы намек на то, что когда компилятор говорит, что не может выбрать одну из N перегрузок, в каждой из которых стоит забористый enable_if_t, то пользователю от этого лучше не становится.


Что-то вы как-то категорично. У меня компилятор просто говорит что не может вызвать метод, т.к. не может найди подходящий с подходящими типами аргументов. Точно также как и в случае с простыми перегрузками функций. Причем он говорит какой именно метод ему нужен, а не где-то в потрохах библиотеки.
Re[2]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 17:25
Оценка:
Здравствуйте, XOOIOOX, Вы писали:

XOO>Я дико извиняюсь, но это же не STL'ные итераторы. Может, если использовать стандартные контейнеры и их итераторы, не придется городить огород?


Тогда что такое по вашему STL-ные итераторы? STL расширяемая библиотека и все что имеет интерфейс итератора им и является, например указатели.
Re[4]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 17:34
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Что ? Сортировка не работает для суммирования? Вроде же ясно написал "отделить алгоритм прохода по итераторам от алгоритма вычисления значения на каждом шаге". Вот у вас есть линейный проход: for_each(..), а есть нелинейный проход: std::binary_search. Есть операции изменения последовательности std::rotate и sort и пр., а есть операции изменения значений std::transform. Почти в каждом случае есть разделение операций над самой последовательностью и операций над данными последовательности. Почти, потому что есть такие функции как find и remove, которые используют прописанные операции сравнения прямо в алгоритме, но даже для них есть возможность переопределить эту операцию для объектов или же использовать вариант с суффиксом _if. Так что я совсем не понимаю ваших претензий.


На практике у меня функция собственно и инкапсулирует алгоритм прохода по итераторам, он не тривиальный и такого в STL быть не может. Функция не доходит до end итератора, она просто проверяет end что бы за конец буфера. Вот реальная функция, если что:
template<typename It, std::enable_if_t<is_const_iterator_of_v<It, char>, int> = 0>
constexpr It NextCodePoint(It str, It last) noexcept;

То-есть вы допускаете в STL существование своих функции вычисления значений и отказываете STL в возможности написания своих функций обхода?
Re[7]: ненависть к итераторам
От: so5team https://stiffstream.com
Дата: 28.12.20 18:04
Оценка:
Здравствуйте, Videoman, Вы писали:

V>У меня такая специфика. Я пишу библиотеки, в том числе, и как их будут использовать мне не известно. На самом деле функций куча и они вызывают друг друга. Я предоставляю базовый функционал, если человек хочет "оптимизировать", например свою реализацию сделать, только отдельную функцию, то он только ее и предоставляет, причем в своем namespace.


Что-то я упускаю, наверное. Но если ваш calc_something живет в вашем namespace, а собственные calc_something он делает в своем, то в чем вообще проблема? Тем более, что если пользовательский calc_something определяется для конкретных типов, то такая перегрузка будет выбрана вместо шаблонной версии.

Я бы еще понял, если бы вы позволяли пользователю переопределять calc_something в вашем namespace...

S>>то почему должно быть запрещено вот такое?

S>>
S>>char * p = ...;
S>>const char * e = p+3;
S>>calc_something(p, e);
S>>


V>А кто сказал что такое запрещено.


Так ведь здесь аргументы двух разных типов: char* и const char*, а у вас оба аргумента принадлежат одному типу It.
Re[8]: ненависть к итераторам
От: Videoman Россия https://hts.tv/
Дата: 28.12.20 18:11
Оценка:
Здравствуйте, so5team, Вы писали:

S>Что-то я упускаю, наверное. Но если ваш calc_something живет в вашем namespace, а собственные calc_something он делает в своем, то в чем вообще проблема? Тем более, что если пользовательский calc_something определяется для конкретных типов, то такая перегрузка будет выбрана вместо шаблонной версии.


S>Я бы еще понял, если бы вы позволяли пользователю переопределять calc_something в вашем namespace...


Ну так ADL же.

S>Так ведь здесь аргументы двух разных типов: char* и const char*, а у вас оба аргумента принадлежат одному типу It.


Ну так у функции calc_something(const char* beg, const char* end) тоже оба типа одинаковых, но вы же туда можете передавать char* и const char* в любых комбинациях.
Re[9]: ненависть к итераторам
От: so5team https://stiffstream.com
Дата: 28.12.20 18:37
Оценка:
Здравствуйте, Videoman, Вы писали:

S>>Что-то я упускаю, наверное. Но если ваш calc_something живет в вашем namespace, а собственные calc_something он делает в своем, то в чем вообще проблема? Тем более, что если пользовательский calc_something определяется для конкретных типов, то такая перегрузка будет выбрана вместо шаблонной версии.


S>>Я бы еще понял, если бы вы позволяли пользователю переопределять calc_something в вашем namespace...


V>Ну так ADL же.


И при чем здесь ADL?

S>>Так ведь здесь аргументы двух разных типов: char* и const char*, а у вас оба аргумента принадлежат одному типу It.


V>Ну так у функции calc_something(const char* beg, const char* end) тоже оба типа одинаковых, но вы же туда можете передавать char* и const char* в любых комбинациях.


Такое ощущение, что вы не пробовали: https://wandbox.org/permlink/KG5xwKFUi56i65vd
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.