boost::adaptors для временного объекта
От: df Россия  
Дата: 14.10.16 10:24
Оценка:
Привет всем.

Хотелось бы примерно следующего:

vector<int> GetData()
{
  return { 1,2,3,4,5,6,7,8,9 }; 
}

auto doubled = GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; });


С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
Re: boost::adaptors для временного объекта
От: swingus  
Дата: 15.10.16 16:51
Оценка:
Оператор | перегружен для адаптора и рэнджа и возвращает рэндж. Рэндж по определению не является владельцем данных, что-то вроде view (так, кстати, это называется в новых Ranges Эрика Неблера). Так что нужно провернуть все действия с адапторами, пока временный объект не разрушился, значит, нужно писать oneliner.


   std::cout << 
    (
        std::vector<int>{ 1,2,3,4,5,6,7,8,9 } | 
        boost::adaptors::transformed
        ([](auto& i) { return i * 2; })
    ) << std::endl;

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

А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?
Re: boost::adaptors для временного объекта
От: push  
Дата: 15.10.16 18:52
Оценка:
Здравствуйте, df, Вы писали:

df>Привет всем.


df>Хотелось бы примерно следующего:

df>

df>vector<int> GetData()
df>{
df>  return { 1,2,3,4,5,6,7,8,9 }; 
df>}

df>auto doubled = GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; });

df>


df>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?


не очень понятна идея, но есть такие варианты:
1) переопределить оператор присваивания — но тогда забыть про auto
2) добавить через вертикальную строку преобразователь в нужный формат (типа как в LINQ для проекции)
3) использовать подобие расширяющих методов в с++: расширяющие методы (вот тут p-strade/oven что-то япошки пилили, но сложно сказать в каком оно сейчас состоянии)
Отредактировано 15.10.2016 18:54 push . Предыдущая версия .
Re: boost::adaptors для временного объекта
От: andyp  
Дата: 15.10.16 19:58
Оценка:
Здравствуйте, df, Вы писали:

df>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?


Сохранить результат в другой контейнер с нужным временем жизни?

vector<int> GetData()
{
  return { 1,2,3,4,5,6,7,8,9 }; 
}
std::vector<int> doubled;
boost::push_back(doubled, GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; }));
Re: boost::adaptors для временного объекта
От: Evgeny.Panasyuk Россия  
Дата: 15.10.16 20:10
Оценка:
Здравствуйте, df, Вы писали:

df>А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?


Придётся либо использовать только в рамках выражения (в котором живёт временная переменная), либо сохранять. Можно сделать обёртку, которая внутри будет хранить и исходный контейнер, и адаптеры (либо уже адаптированный range), и сама обёртка при этом будет является range. То есть примерно:
auto doubled = make_wrapper_owner(GetData(), transformed(_1 * 2));
Отредактировано 15.10.2016 20:11 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 15.10.2016 20:11 Evgeny.Panasyuk . Предыдущая версия .
Re[2]: boost::adaptors для временного объекта
От: df Россия  
Дата: 17.10.16 10:55
Оценка:
Здравствуйте, swingus, Вы писали:

S>что-то вроде view (так, кстати, это называется в новых Ranges Эрика Неблера).


А эти его "новые" имеют отношения к бусту? Можно чуть подробнее об этом?


S>Так что нужно провернуть все действия с адапторами, пока временный объект не разрушился, значит, нужно писать oneliner.


Это понятно. Думал, мало-ли хитрость какую придумали, в рамках того-же буста.
Re[2]: boost::adaptors для временного объекта
От: df Россия  
Дата: 17.10.16 11:09
Оценка:
Здравствуйте, andyp, Вы писали:


df>>С этим диапазоном сделать мы ничего не сможем, т.к. адаптер будет применяться к временному объекту (коряво, но идея, думаю, понятна). А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?


A>Сохранить результат в другой контейнер с нужным временем жизни?


A>
A>vector<int> GetData()
A>{
A>  return { 1,2,3,4,5,6,7,8,9 }; 
A>}
A>std::vector<int> doubled;
A>boost::push_back(doubled, GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; }));
A>


в итоге так и сделал. Городить ничего не стал. Смысла нет. Просто подумал вдруг это уже как-то решено (типа обертка какая есть), а я не знаю.
Re[2]: boost::adaptors для временного объекта
От: df Россия  
Дата: 17.10.16 11:14
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


df>>А как бы все-таки обмануть? Без того, чтобы предварительно сохранять результат GetData()?


EP>Придётся либо использовать только в рамках выражения (в котором живёт временная переменная), либо сохранять. Можно сделать обёртку, которая внутри будет хранить и исходный контейнер, и адаптеры (либо уже адаптированный range), и сама обёртка при этом будет является range. То есть примерно:

EP>
EP>auto doubled = make_wrapper_owner(GetData(), transformed(_1 * 2));
EP>


Думал мало-ли есть уже готовое что-то подобное. Но, наверное, не очень актуально это.
ПС: а без лямбды гораздо лаконичнее смотрится, спасибо. А то по привычке стал вставлять ее везде.
Re[3]: boost::adaptors для временного объекта
От: swingus  
Дата: 17.10.16 15:19
Оценка:
df>А эти его "новые" имеют отношения к бусту? Можно чуть подробнее об этом?

Они относятся к STL, сейчас включены в Technical Preview, требуют C++ 14 (может быть даже 17, но есть порт на VC++). Репозиторий, порт на VC++
Re[3]: boost::adaptors для временного объекта
От: Evgeny.Panasyuk Россия  
Дата: 17.10.16 20:31
Оценка:
Здравствуйте, df, Вы писали:

df>Думал мало-ли есть уже готовое что-то подобное. Но, наверное, не очень актуально это.


Не встречал. Момент не приятный, да.

df>ПС: а без лямбды гораздо лаконичнее смотрится, спасибо. А то по привычке стал вставлять ее везде.


Это самый лаконичный вариант, лаконичнее наверно не придумаешь (чуть короче можно через карринг — * 2, но это менее читаемо, ИМХО).

df>ПС: а без лямбды гораздо лаконичнее смотрится, спасибо.


Но время компиляции с Boost.Phoenix (либо Boost.Lambda) намного больше.
Кстати, товарищ jazzer показывал трюк
Автор: jazzer
Дата: 30.10.13
как малыми усилиями получить подобное через std::bind, в котором expression tree уже встроено.
Re[2]: boost::adaptors для временного объекта
От: Evgeny.Panasyuk Россия  
Дата: 17.10.16 20:35
Оценка:
Здравствуйте, andyp, Вы писали:

A>Сохранить результат в другой контейнер с нужным временем жизни?

A>
A>vector<int> GetData()
A>{
A>  return { 1,2,3,4,5,6,7,8,9 }; 
A>}
A>std::vector<int> doubled;
A>boost::push_back(doubled, GetData() | boost::adaptors::transformed([](auto& i) {return i * 2; }));
A>


Так весь же смысл transformed именно в ленивости. Энергично можно и через std/boost::transform/range-based-for посчитать.
Re[3]: boost::adaptors для временного объекта
От: andyp  
Дата: 17.10.16 20:58
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Так весь же смысл transformed именно в ленивости. Энергично можно и через std/boost::transform/range-based-for посчитать.


Ну где-то ж ленивость должна закончиться? У ТС она очевидно должна закончиться, пока живет вектор, который он вернул из GetData(), т.е. до ;. Ничего с этим не поделаешь
Re[4]: boost::adaptors для временного объекта
От: Evgeny.Panasyuk Россия  
Дата: 17.10.16 21:43
Оценка:
Здравствуйте, andyp, Вы писали:

EP>>Так весь же смысл transformed именно в ленивости. Энергично можно и через std/boost::transform/range-based-for посчитать.

A>Ну где-то ж ленивость должна закончиться? У ТС она очевидно должна закончиться, пока живет вектор, который он вернул из GetData(), т.е. до ;. Ничего с этим не поделаешь

Необязательно. Можно сделать обёртку, которая переместит вектор и адаптеры во внутрь себя, и при этом сама будет являться range, схематично:
template< ..., typename LazyRange>
class wrapper
{
    vector<...> inner_vector;
    LazyRange lazy_range;
public:
    ...
    auto begin() const ...;
    auto end() const ...;
};
Вектор будет жить столько, сколько будет жить обёртка — её можно даже вернуть вверх по стэку.

Если совсем не оптимально, то можно ещё вот так проиллюстрировать:
auto shared_data = make_shared<vector<int>>( GetData() );
auto doubled = *shared_data | boost::adaptors::transformed([shared_data](auto i){ return i * 2; });
doubled можно вернуть вверх по стэку.
Отредактировано 17.10.2016 21:47 Evgeny.Panasyuk . Предыдущая версия . Еще …
Отредактировано 17.10.2016 21:45 Evgeny.Panasyuk . Предыдущая версия .
Отредактировано 17.10.2016 21:44 Evgeny.Panasyuk . Предыдущая версия .
Re[5]: boost::adaptors для временного объекта
От: andyp  
Дата: 18.10.16 08:05
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Необязательно. Можно сделать обёртку, которая переместит вектор и адаптеры во внутрь себя, и при этом сама будет являться range, схематично:

  code
EP>
EP>template< ..., typename LazyRange>
EP>class wrapper
EP>{
EP>    vector<...> inner_vector;
EP>    LazyRange lazy_range;
EP>public:
EP>    ...
EP>    auto begin() const ...;
EP>    auto end() const ...;
EP>};
EP>

Вектор будет жить столько, сколько будет жить обёртка — её можно даже вернуть вверх по стэку.

EP>Если совсем не оптимально, то можно ещё вот так проиллюстрировать:

  code
EP>
EP>auto shared_data = make_shared<vector<int>>( GetData() );
EP>auto doubled = *shared_data | boost::adaptors::transformed([shared_data](auto i){ return i * 2; });
EP>

doubled можно вернуть вверх по стэку.

Все так, но судя по всему (тут вангую) ТС именно этого и хотел избежать. Можно ж было просто

const std::vector<int>& data = GetData();

//тут строим нужный пайплайн из вьюшек


сделать. Вот жизнь исходного вектора и продлена.
Re[6]: boost::adaptors для временного объекта
От: df Россия  
Дата: 18.10.16 08:43
Оценка:
Здравствуйте, andyp, Вы писали:


A>Все так, но судя по всему (тут вангую) ТС именно этого и хотел избежать. Можно ж было просто


A>
A>const std::vector<int>& data = GetData();

A>//тут строим нужный пайплайн из вьюшек
A>


A>сделать.


Именно так.
Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер:

auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })


выглядит нормально имхо. Но суть от этого не меняется (причем в идеале holder должен уметь отличать rvalue от lvalue).
Как мне кажется, делать такое _самому_ лишено большого смысла. А вот если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.
Re[7]: boost::adaptors для временного объекта
От: andyp  
Дата: 18.10.16 09:48
Оценка:
Здравствуйте, df, Вы писали:

df>Именно так.

df>Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер:

df>
df>auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })
df>


df>выглядит нормально имхо. Но суть от этого не меняется (причем в идеале holder должен уметь отличать rvalue от lvalue).

df>Как мне кажется, делать такое _самому_ лишено большого смысла. А вот если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.


На сколько понял, по факту тебе требуется нечто типа позднего связывания range и последовательности, чтобы иметь возможность определить алгоритм обработки элементов диапазона заранее, а затем в месте вызова связать его с контейнером или другим range. Не уверен, что требуемый адаптер существует в boost. Т.е. получить поведение типа:


auto r = make_unbound_range() | boost::adaptors::transformed([](auto& i) {return i * 2; });

std::vector<int> doubled;
boost::push_back(doubled, r | bind_range(getData()));


Можно ли это прикрутить малой кровью, я не знаю Может Евгений что скажет.
Re[8]: boost::adaptors для временного объекта
От: df Россия  
Дата: 18.10.16 11:03
Оценка:
Здравствуйте, andyp, Вы писали:


df>>Причем хотелось _бы_ именно _готовое_ решение. Потому как понятно, что _руками_ красоту можно навести. Т.е. вполне можно было бы написать такой враппер:

df>>
df>>auto r = GetData() | holder | boost::adaptors::transformed([](auto& i) {return i * 2; })
df>>

df>>если бы было готовое в рамках того же буста, к примеру, то почему бы не воспользоваться.

A>На сколько понял, по факту тебе требуется нечто типа позднего связывания range и последовательности, чтобы иметь возможность определить алгоритм обработки элементов диапазона заранее, а затем в месте вызова связать его с контейнером или другим range. Не уверен, что требуемый адаптер существует в boost.


Скорее применение адаптеров к rvalue.
Основной смысл вопроса был в том существует-ли что-либо готовое. Нет, понятно.
А все остальное, повторюсь, "овчинка выделки не стоит". Ну или академический интерес (тоже хорошо).
Вы правильно сказали, писать враппер только для того, чтобы скрыть в нем холдер (вектор)... не уверен.


A>
A>auto r = make_unbound_range() | boost::adaptors::transformed([](auto& i) {return i * 2; });

A>std::vector<int> doubled;
A>boost::push_back(doubled, r | bind_range(getData()));

A>


A>Можно ли это прикрутить малой кровью, я не знаю Может Евгений что скажет.
Re[6]: boost::adaptors для временного объекта
От: Evgeny.Panasyuk Россия  
Дата: 18.10.16 15:46
Оценка:
Здравствуйте, andyp, Вы писали:

A>Все так, но судя по всему (тут вангую) ТС именно этого и хотел избежать. Можно ж было просто

A>
A>const std::vector<int>& data = GetData();

A>//тут строим нужный пайплайн из вьюшек
A>

A>сделать. Вот жизнь исходного вектора и продлена.

Да, само собой, я про случай когда этому vector+range нужно пережить текущий scope, например return вверх по стэку.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.