Можно ли и как канонично получить из итератора тип элемента последовательности?
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 17:54
Оценка:
Здравствуйте!

Вот у меня есть класс UtfIterator, который при разыменовании возвращает 32ух-битный Unicode символ, разбирая UTF8 последовательность по char указателю, который хранит у себя, и соответственно, всякие игры c &*it выдадут лажу.

Также у меня в те же функции может быть передан итератор от вектора char или строки, или вообще голые указатели.

Я хочу из любого типа итераторов одинаковым способом получить const char*.

Есть ли для этого каноничное решение?
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: vopl Россия  
Дата: 17.04.25 18:22
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


M>Вот у меня есть класс UtfIterator, который при разыменовании возвращает 32ух-битный Unicode символ, разбирая UTF8 последовательность по char указателю, который хранит у себя, и соответственно, всякие игры c &*it выдадут лажу.


M>Также у меня в те же функции может быть передан итератор от вектора char или строки, или вообще голые указатели.


M>Я хочу из любого типа итераторов одинаковым способом получить const char*.


M>Есть ли для этого каноничное решение?


Чтобы продать что-нибудь ненужное, нужно сначала купить что-нибудь ненужное, а у нас денег нет
Чтобы откуда ни будь получить const char* он там изначально должен быть, а у тебя там 32ух-битный Unicode символ. Вот его вполне можно и получить
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 18:26
Оценка:
Здравствуйте, vopl, Вы писали:

V>Чтобы откуда ни будь получить const char* он там изначально должен быть, а у тебя там 32ух-битный Unicode символ. Вот его вполне можно и получить


У меня в итераторе const char* хранится, 32ух-битный Unicode символ собирается по UTF8-строке и при разыменовании возвращается по значению
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: gyraboo  
Дата: 17.04.25 18:27
Оценка:
Здравствуйте, Marty, Вы писали:

M>Есть ли для этого каноничное решение?


Если речь про ООП, то там канон вообще против знаний о типе в рантайме.
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 18:30
Оценка:
Здравствуйте, gyraboo, Вы писали:

M>>Есть ли для этого каноничное решение?


G>Если речь про ООП, то там канон вообще против знаний о типе в рантайме.


Не знаю, что ты имеешь в виду, но я хочу в компайл тайме это сделать
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: T4r4sB Россия  
Дата: 17.04.25 19:33
Оценка:
Здравствуйте, Marty, Вы писали:

M>Здравствуйте!


M>Вот у меня есть класс UtfIterator, который при разыменовании возвращает 32ух-битный Unicode символ, разбирая UTF8 последовательность по char указателю, который хранит у себя, и соответственно, всякие игры c &*it выдадут лажу.


M>Также у меня в те же функции может быть передан итератор от вектора char или строки, или вообще голые указатели.


M>Я хочу из любого типа итераторов одинаковым способом получить const char*.


M>Есть ли для этого каноничное решение?


Написать перегрузки?
void foo(UtfIterator);
void foo(std::vector<char>::iterator);
void foo(std::string::iterator);
void foo(const char*);

внутри там уже можно одну к другой привести чтоб код не дублировать
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 19:46
Оценка:
Здравствуйте, T4r4sB, Вы писали:

M>>Есть ли для этого каноничное решение?


TB>Написать перегрузки?


Пока так и решил сделать, но посчитал нужным спросить, вдруг есть стандартное решение
Маньяк Робокряк колесит по городу
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 19:50
Оценка:
Здравствуйте, T4r4sB, Вы писали:

M>>Есть ли для этого каноничное решение?


TB>Написать перегрузки?


Но вот такое не работает, компилятор не может понять, чего я от него хочу
//----------------------------------------------------------------------------
template<typename CharType>
const char* rawConstCharPtrFromIterator(marty::utf::UtfInputIterator<CharType> it)
{
    return (const char*)it.rawPtr();
}

//----------------------------------------------------------------------------
template<typename CharType>
const char* rawConstCharPtrFromIterator(const CharType *pstr)
{
    return (const char*)pstr;
}

//----------------------------------------------------------------------------
template<typename ContainerType>
const char* rawConstCharPtrFromIterator(typename ContainerType::const_iterator it)
{
    return (const char*)(&*it);
}
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 20:13
Оценка:
Здравствуйте, Marty, Вы писали:

Попробовал по-разному, последнее такое:

//----------------------------------------------------------------------------
template<typename CharType>
const char* rawConstCharPtrFromIterator(marty::utf::UtfInputIterator<CharType> it)
{
    return (const char*)it.rawPtr();
}

//----------------------------------------------------------------------------
template<typename CharPointerType>
typename std::enable_if< std::is_pointer<CharPointerType>::value, const char* >::type
rawConstCharPtrFromIterator(CharPointerType pstr)
{
    return (const char*)pstr;
}

//----------------------------------------------------------------------------
template<typename ContainerType>
typename std::enable_if< has_const_iterator<ContainerType>::value, const char* >::type
rawConstCharPtrFromIterator(typename ContainerType::const_iterator it)
{
    return (const char*)(&*it);
}



Никак не понимает, что я хочу. Как объяснить?
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 20:51
Оценка:
Здравствуйте, Marty, Вы писали:

Написал предельно тупо:
template<typename CharType>
const char* rawConstCharPtrFromIterator(marty::utf::UtfInputIterator<CharType> it)
{
    return (const char*)it.rawPtr();
}

template<typename CharType>
const char* rawConstCharPtrFromIterator(typename std::basic_string<CharType>::iterator it)
{
    return (const char*)(&*it);
}

template<typename CharType>
const char* rawConstCharPtrFromIterator(typename std::basic_string<CharType>::const_iterator it)
{
    return (const char*)(&*it);
}


Но компилятор меня всё равно не понимает
Маньяк Робокряк колесит по городу
Re[2]: Можно ли и как канонично получить из итератора тип эл
От: rg45 СССР  
Дата: 17.04.25 22:25
Оценка:
Здравствуйте, Marty, Вы писали:

M>Попробовал по-разному, последнее такое:


M>
M>//----------------------------------------------------------------------------
M>template<typename CharType>
M>const char* rawConstCharPtrFromIterator(marty::utf::UtfInputIterator<CharType> it)
M>{
M>    return (const char*)it.rawPtr();
M>}

M>//----------------------------------------------------------------------------
M>template<typename CharPointerType>
M>typename std::enable_if< std::is_pointer<CharPointerType>::value, const char* >::type
M>rawConstCharPtrFromIterator(CharPointerType pstr)
M>{
M>    return (const char*)pstr;
M>}

M>//----------------------------------------------------------------------------
M>template<typename ContainerType>
M>typename std::enable_if< has_const_iterator<ContainerType>::value, const char* >::type
M>rawConstCharPtrFromIterator(typename ContainerType::const_iterator it)
M>{
M>    return (const char*)(&*it);
M>}

M>



M>Никак не понимает, что я хочу. Как объяснить?


Ну так конечно, у тебя же последняя перегрузка невыводимая в принципе. Ты прикинь, одновременно 100500 классов контейнеров могут иметь одинаковые типы итераторов и какой тип контейнера должен при этом вывестись?

На С++20 эта задача решается легко и изящно:

template <typename T>
concept Dereferenceable = requires(T t) {{&*t} -> std::convertible_to<const void*>;};

template <typename T>
concept Pointer = Dereferenceable<T> and std::convertible_to<T, const void*>;

const char* rawConstCharPtrFromIterator(auto&& t)
requires requires { (const char*)t.rawPtr(); }
{
   return (const char*)t.rawPtr();
}

const char* rawConstCharPtrFromIterator(Pointer auto&& t)
{
   return (const char*)t;
}

const char* rawConstCharPtrFromIterator(Dereferenceable auto&& t)
{
   return (const char*)(&*t);
}


Можно сделать аналог и на SFINAE, но там чуть больше пыли глотать придётся.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 17.04.2025 22:29 rg45 . Предыдущая версия .
Re[3]: Можно ли и как канонично получить из итератора тип эл
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 17.04.25 23:04
Оценка:
Здравствуйте, rg45, Вы писали:

M>>Никак не понимает, что я хочу. Как объяснить?


R>Ну так конечно, у тебя же последняя перегрузка невыводимая в принципе.


Ну я не настоящий сварщик


R>Ты прикинь, одновременно 100500 классов контейнеров могут иметь одинаковые типы итераторов и какой тип контейнера должен при этом вывестись?


А мне на тип контейнера наплевать, мне интересен тип, который получается при разыменовании ContainerType::iterator, при этом, если мне дали итератор не последовательногопроизвольного доступа, и я начал с ним работать — эта проблема меня не волнует, я не хочу парится с детектом такой ситуации.

Хотя, был наверное не прав в формулировках. Я исхожу из той концепции, что итератор либо указывает на реальную память, и при разыменовании возвращает что-то, с чем можно сделать &*it, и какие-то другие типы итераторов, которые надо отдельно обрабатывать.

Тут подумалось, можно попробовать:
— является ли итератор простым указателем
— можно ли сделать &*it
— огород для других типов итераторов

но не уверен, что компилятор меня поймёт, да и как реализовать, пока не очень понимаю.

Наверное, надо копнуть на тему enable_if c decltype/decay по типу *it


R>На С++20 эта задача решается легко и изящно:


А на С++17?

Мне бы хотелось привести к указателю на char
— итератор любого стандартного контейнера с произвольным доступом, который содержит элементы типа char или интегрального тип такого же размера (по размеру — желательно, но не обязательно)
— сам char* или const char*
— свой итератор, который при разыменовании возвращает не ссылку на элемент по хранимому в нем char-указателю, а произвольное значение by value. У последнего типа итератора есть метод rawPtr


R>Можно сделать аналог и на SFINAE, но там чуть больше пыли глотать придётся.


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

//----------------------------------------------------------------------------
template<typename CharType>
const char* rawConstCharPtrFromIterator(marty::utf::UtfInputIterator<CharType> it)
{
    return (const char*)it.rawPtr();
}

template<typename CharType>
const char* rawConstCharPtrFromIterator(typename std::basic_string<CharType>::iterator it)
{
    return (const char*)(&*it);
}

template<typename CharType>
const char* rawConstCharPtrFromIterator(typename std::basic_string<CharType>::const_iterator it)
{
    return (const char*)(&*it);
}
Маньяк Робокряк колесит по городу
Отредактировано 17.04.2025 23:18 Marty . Предыдущая версия . Еще …
Отредактировано 17.04.2025 23:07 Marty . Предыдущая версия .
Re[4]: Можно ли и как канонично получить из итератора тип эл
От: rg45 СССР  
Дата: 18.04.25 07:47
Оценка: 12 (1)
Здравствуйте, Marty, Вы писали:

R>>Можно сделать аналог и на SFINAE, но там чуть больше пыли глотать придётся.

M>Ну, намекни, куда копать, мне пока не понятно. Сделал совсем тупо для базовых кейсов — тоже не прокатило:

Ну, как-то так. И обрати внимание, если ты хочешь, чтоб работал ADL, перегрузку rawConstCharPtrFromIterator для UtfInputIterator нужно внести в неймспейс marty::utf.

https://coliru.stacked-crooked.com/a/456031d81cb952ce

#include <cstdint>
#include <iostream>
#include <type_traits>

template<typename, typename = void>
struct IsDereferenceableToLvalue_ : std::false_type {};

template<typename T>
struct IsDereferenceableToLvalue_<T, std::void_t<decltype(&*std::declval<T>())>> : std::true_type {};

template<typename T>
inline constexpr bool IsDereferenceableToLvalue = IsDereferenceableToLvalue_<T>::value;

template<typename T>
inline constexpr bool IsPointer = std::is_pointer_v<std::decay_t<T>>;

namespace marty {
namespace utf {

template <typename CharType>
struct UtfInputIterator
{
   const CharType& operator*() const;
   const CharType* rawPtr() const {return reinterpret_cast<const CharType*>("marty::utf::UtfInputIterator"); };
};

template<typename CharType>
const char* rawConstCharPtrFromIterator(UtfInputIterator<CharType> it)
{
   return reinterpret_cast<const char*>(it.rawPtr());
}

} // namespace utf
} // namespace marty

template<typename CharPointerType>
const char* rawConstCharPtrFromIterator(CharPointerType* pstr)
{
   return reinterpret_cast<const char*>(pstr);
}

template <typename IteratorType, std::enable_if_t<
    IsDereferenceableToLvalue<IteratorType>
    and not IsPointer<IteratorType>
, int> = 0>
const char* rawConstCharPtrFromIterator(IteratorType it)
{
   return reinterpret_cast<const char*>(&*it);
}

int main()
{
   const char* const c = "const char*";
   std::string const s = "std::string";
   marty::utf::UtfInputIterator<uint32_t> const it{};

   std::cout << rawConstCharPtrFromIterator(c) << std::endl;
   std::cout << rawConstCharPtrFromIterator(s.begin()) << std::endl;
   std::cout << rawConstCharPtrFromIterator(it) << std::endl;
}
--
Справедливость выше закона. А человечность выше справедливости.
Re: Можно ли и как канонично получить из итератора тип элеме
От: rg45 СССР  
Дата: 18.04.25 08:36
Оценка: +1
Здравствуйте, Marty, Вы писали:

M>Можно ли и как канонично получить из итератора тип элемента последовательности?

M>Я хочу из любого типа итераторов одинаковым способом получить const char*.

Это две совершенно разные задачи. Вторая решаема (я показал
Автор: rg45
Дата: 18.04.25
вариант решения).

Первая же задача нерешаема в принципе. Давай рассмотрим такой псевдокод:

// Имеем несколько классов контейнеров.
// Каждый класс содержит внутри определения своих итераторов.
// И разумеется уникальность типов итераторов никто не гарантирует.

struct Containter1 { using iterator = int*; };
struct Containter2 { using iterator = int*; };
struct Containter3 { using iterator = int*; };

// Допустим, что по типу итератора можно вывести тип контейнера.
template <typename ContainerType>
void foo(ContainerType::iterator);

int main()
{
   // Попоробуем заюзать это дело.
   Container1::iterator it1;
   Container2::iterator it2;
   Container3::iterator it3;

   // Вот в этом месте самое время обратить внимание на то,
   // что объекты it1, it2, it3 имеют один и тот же тип - int*.

   // И каким образом компилятор должен понять, что в следующих
   // случаях должны быть выведены различные типы контейнеров и какие именно?
   foo(it1);
   foo(it2);
   foo(it3);
}
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 18.04.2025 8:49 rg45 . Предыдущая версия . Еще …
Отредактировано 18.04.2025 8:38 rg45 . Предыдущая версия .
Отредактировано 18.04.2025 8:36 rg45 . Предыдущая версия .
Re[5]: Можно ли и как канонично получить из итератора тип эл
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 18.04.25 14:11
Оценка:
Здравствуйте, rg45, Вы писали:

R>>>Можно сделать аналог и на SFINAE, но там чуть больше пыли глотать придётся.

M>>Ну, намекни, куда копать, мне пока не понятно. Сделал совсем тупо для базовых кейсов — тоже не прокатило:

R>Ну, как-то так. И обрати внимание, если ты хочешь, чтоб работал ADL, перегрузку rawConstCharPtrFromIterator для UtfInputIterator нужно внести в неймспейс marty::utf.


R>https://coliru.stacked-crooked.com/a/456031d81cb952ce


Круто. Спасибо
Маньяк Робокряк колесит по городу
Re: Можно ли и как канонично получить из итератора тип элемента последовательнос
От: B0FEE664  
Дата: 18.04.25 17:13
Оценка:
Здравствуйте, Marty, Вы писали:

M>Я хочу из любого типа итераторов одинаковым способом получить const char*.

M>Есть ли для этого каноничное решение?
Я бы добавил итератору метод
const char* base()
(по аналогии с std::reverse_iterator)

Я так понимаю, все UtfIterator'ы только константные и поддержки *it = 'a' не будет? Вот без этого не интересно.
И каждый день — без права на ошибку...
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: rg45 СССР  
Дата: 18.04.25 17:37
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Я так понимаю, все UtfIterator'ы только константные и поддержки *it = 'a' не будет? Вот без этого не интересно.


Ну почему, само название UtfInputIterator намекает на существование UtfOutputIterator.
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: Можно ли и как канонично получить из итератора тип элемента последователь
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 18.04.25 18:18
Оценка:
Здравствуйте, B0FEE664, Вы писали:

M>>Я хочу из любого типа итераторов одинаковым способом получить const char*.

M>>Есть ли для этого каноничное решение?
BFE>Я бы добавил итератору метод const char* base() (по аналогии с std::reverse_iterator)

Ну, rawPtr() таки возвращает не то, что base() в std::reverse_iterator, и я не хочу навешивать на стандартное имя метода то, что оно не делает — проверки по SFINAE или новомодные концепты могут детектить не то, что бы им хотелось на самом деле.

Но begin()/end() есть, мой итератор может выглядеть как range контейнер, от текущего указателя на char'ы до заданного конца


BFE>Я так понимаю, все UtfIterator'ы только константные и поддержки *it = 'a' не будет? Вот без этого не интересно.


Для вывода есть UtfOutputIterator, из InputIterator в OutputIterator можно скопировать как while(inB!=inE) *outIt++ = *inB++; , так и при помощи std алгоритмов: std::copy(inB, inE, outIt), или, при нужде что-то поменять, std::transform, как вариант.

Модифицировать исходные строки, вставляя/заменяя туда/там Unicode символы, которые могут иметь длину от одного до шести байт в UTF-8 — не вижу смысла — несколько замен/вставок в начале/середине строки могут как привести к переаллокации, так и гарантированно приведут к перемещениям хвостов контейнеров, в которых это хранится. Овчинка выделки не стоит, и даже вредна. Лучше для результата зарезервировать место один раз, с возможным запасом на результат модификации, это будет эффективнее.
Маньяк Робокряк колесит по городу
Re[3]: Можно ли и как канонично получить из итератора тип элемента последователь
От: B0FEE664  
Дата: 22.04.25 10:30
Оценка:
Здравствуйте, rg45, Вы писали:

BFE>>Я так понимаю, все UtfIterator'ы только константные и поддержки *it = 'a' не будет? Вот без этого не интересно.

R>Ну почему, само название UtfInputIterator намекает на существование UtfOutputIterator.

OutputIterator — это относительно просто, а вот изменение по месту — нетривиальная задача, от воплощения которой Marty тут же отказался. По работе я не сталкиваюсь с редакторами текста, так что для меня задача не актуальна и я не могу оценить востребованность такой операции, так что может оно и не надо...
И каждый день — без права на ошибку...
Re[4]: Можно ли и как канонично получить из итератора тип эл
От: rg45 СССР  
Дата: 22.04.25 11:12
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>OutputIterator — это относительно просто, а вот изменение по месту — нетривиальная задача, от воплощения которой Marty тут же отказался. По работе я не сталкиваюсь с редакторами текста, так что для меня задача не актуальна и я не могу оценить востребованность такой операции, так что может оно и не надо...


Так output iterator requirements включают в себя то самое изменение по месту:

A LegacyOutputIterator is a LegacyIterator that can write to the pointed-to element.


Это же его основной сценарий использования. Без этого стандартные алгоритмы просто не работали бы.
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 22.04.2025 11:17 rg45 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.