LINUX.ORG.RU

Избежать повторения кода с помощью C++11 range-based for

 ,


5

6

Допустим, есть какие-то единообразные инструкции над несколькими переменными

if(x % 2 == 0) ++x;
if(y % 2 == 0) ++y;
if(z % 2 == 0) ++z;

Как такое делается в современном c++? Я попробовал range-based for, но заработал только вариант с указателями.

for(auto t: {&x, &y, &z})
    if((*t) % 2 == 0) ++(*t);

Reference/dereference тут выглядит явно неуместно. Можно как-то по-другому?

Update

С помощью анонимусов получилось вот такое решение

#include <cstdio>
#include <initializer_list>
#include <functional>

#define rlist(x, ...) (std::initializer_list<std::reference_wrapper<decltype(x)>>({x, __VA_ARGS__}))

int main()
{
    int x = 2, y = 3, z = 4;
    for(auto t : rlist(x, y, z)) if(t % 2 == 0) ++t;
    printf("%d %d %d\n", x, y, z);
    return 0;
}

При использовнии выглядит достаточно прилично (а на макрос можно не смотреть). Требует стандарта C++11. Что с производительностью - пока не знаю.

Update 2

Решение на шаблонах от eao197

#include <iostream>
using namespace std;

template<typename F> void apply_to_all(F &&) {}

template<typename F, typename T, typename... O>
void apply_to_all(F && f, T && x, O && ...other) {
	f(forward<T>(x));
	apply_to_all(forward<F>(f), forward<O>(other)...);
}

int main() {
	int x = 2, y = 3, z = 4, v = 5, w = 6;
        apply_to_all([](int & v) { if(v % 2 == 0) ++v; },
		     x, y, z, v, w);
        apply_to_all([](int v) { cout << v << " "; },
		     x, y, z, v, w);
        cout << endl;
        return 0;
}
На c++14 можно писать auto вместо int в лямбде.

В С++17 за счет if constexpr не нужно будет делать пустую заглушку для прекращения рекурсии вызовов:

template<typename F, typename T, typename... O>
void apply_to_all(F && f, T && x, O && ...other) {
	f(forward<T>(x));
	if constexpr(0 != sizeof...(other))
		apply_to_all(forward<F>(f), forward<O>(other)...);
}

★★★★★

Последнее исправление: Crocodoom (всего исправлений: 3)

Ответ на: комментарий от CrossFire

каждый программист будет писать на том подмножестве которое он знает

А корпоративные стандарты на что? Применил темплейты - уволен!

anonymous
()
Ответ на: комментарий от Im_not_a_robot

Да и мини циклы обычно не труе в плане оптимизации.

fornlr ★★★★★
()
Ответ на: комментарий от anonymous

В Qt проектах достаточно запретить STL, в таком случае большую часть магии использовать не получится и все будет хорошо.

CrossFire ★★★★★
()
Ответ на: комментарий от anonymous

А «#define VARLIST(AA) AA(x) AA(y) AA(z)» это детский сад.

Нужно только если ты используешь один и тот же список в нескольких местах. Если только в одном месте, то

#define DOFOO(var) if (var % 2 == 0) ++var
DOFOO(x);
DOFOO(y);
DOFOO(z);
#undef DOFOO
Кресты близко не валялись по лаконичности, читаемости и жестким требованиям к свежести компилятора.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

А потом кто-то пишет DOFOO(x+y) и поминает вас незлым тихим словом. Потому что макросы в C гораздо хитрее, чем это кажется плюсохейтерам.

eao197 ★★★★★
()
Ответ на: комментарий от kawaii_neko

Кресты близко не валялись по лаконичности, читаемости и жестким требованиям к свежести компилятора.

#include <stdio.h>

int main() {
    int x, y, z = 1;
  
    #define DOFOO(var) if (var % 2 == 0) ++var

    DOFOO( x + y );

    if(0);
    else if(1)
        DOFOO(z);
    else if(1)
        printf("WTF?\n");
}

Против:

auto do_foo = []( auto& var ){ if (var % 2 == 0) ++var; };
do_foo(x);
do_foo(y);
do_foo(z);

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

anonymous
()
Ответ на: комментарий от anonymous

А по поводу компиляторов - ты не найдешь живой дистрибутив, где этот код не соберется.

Хотя нет, для этого надо явно тип написать, например, int&, но то не критично.

anonymous
()
Ответ на: комментарий от eao197

А потом кто-то пишет DOFOO(x+y) и поминает вас незлым тихим словом. Потому что макросы в C гораздо хитрее, чем это кажется плюсохейтерам.

Ок.

#define DOFOO(var) do { if ((var) % 2 == 0) ++(var); } while (0)
Еще чего придумаешь?

kawaii_neko ★★★★
()
Ответ на: комментарий от anonymous

А по поводу компиляторов - ты не найдешь живой дистрибутив, где этот код не соберется.

ubuntu 16.04:

$ g++ -Wall 1.cpp 
1.cpp: In function ‘int main()’:
1.cpp:4:5: warning: ‘auto’ changes meaning in C++11; please remove it [-Wc++0x-compat]
     auto do_foo = []( auto& var ){ if (var % 2 == 0) ++var; };
     ^
1.cpp:4:10: error: ‘do_foo’ does not name a type
     auto do_foo = []( auto& var ){ if (var % 2 == 0) ++var; };
          ^
1.cpp:5:13: error: ‘do_foo’ was not declared in this scope
     do_foo(x);
C++11 в 2017 году до сих пор не является стандартом компиляции по умолчанию.

Ах да, а потом это говнецо же не запустится на системе, где нет libstdc++ с правильным ABI.

UPD:

p$ g++ -Wall 1.cpp -std=c++11
1.cpp: In function ‘int main()’:
1.cpp:4:23: error: use of ‘auto’ in lambda parameter declaration only available with -std=c++14 or -std=gnu++14
     auto do_foo = []( auto& var ){ if (var % 2 == 0) ++var; };

kawaii_neko ★★★★
()
Последнее исправление: kawaii_neko (всего исправлений: 2)
Ответ на: комментарий от kawaii_neko

А чего тут еще придумывать? Достаточно сравнить:

#define DOFOO(var) do { if ((var) % 2 == 0) ++(var); } while (0)
...
DOFOO(x);
DOFOO(y);
DOFOO(z);
...
#undef DOFOO
и
const auto dofoo = [](int & x) { if(x % 2 == 0) ++x; };
...
dofoo(x);
dofoo(y);
dofoo(z);
...
И по количеству символов, и по количеству неявных нюансов, и по типобезопасности плюсовый вариант очевиднее и надежнее.

eao197 ★★★★★
()
Ответ на: комментарий от kawaii_neko

C++11 в 2017 году до сих пор не является стандартом компиляции по умолчанию.

Вообще-то в gcc сейчас по дефолту С++14, добро пожаловать в 2017-й.

Ах да, а потом это говнецо же не запустится на системе, где нет libstdc++ с правильным ABI.

Да, как и код на С, который слинкован на glibc, имеет все шансы не запуститься на системе с более ранней версией glibc:

http://tipsarea.com/2017/03/24/how-to-solve-the-glibc-versoin-not-found-issue/

UPD:

Выше же написано.

anonymous
()
Ответ на: комментарий от anonymous

Да, как и код на С, который слинкован на glibc, имеет все шансы не запуститься на системе с более ранней версией glibc

Имеет, если в коде использованы какие-то новшества из свежей версии glibc. А ABI, связанный с 11-м стандартом, ломали буквально в прошлом году (по крайне мере, на моем gentoo-шном локалхосте).

Вообще-то в gcc сейчас по дефолту С++14, добро пожаловать в 2017-й.

Ну и пацаны gcc прямо из git ставят — чего заморачиваться с дистрибутивным старьем?

kawaii_neko ★★★★
()
Ответ на: комментарий от eao197

И по количеству символов

И тут внезапно потребовалось выполнять те же самые операции над long. Как будут обстоять дела с количеством символов в этом случае? Да и вообще-то уже говорили, что раз гонишься за количеством символов, замени это на |= 1, и никаких макросов и лямбд не надо.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

И тут внезапно потребовалось выполнять те же самые операции над long.

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

eao197 ★★★★★
()
Ответ на: комментарий от kawaii_neko

Имеет, если в коде использованы какие-то новшества из свежей версии glibc.

Ага, вот так вот взяли и memcpy внезапно завезли.

Ну и пацаны gcc прямо из git ставят — чего заморачиваться с дистрибутивным старьем?

Вообще-то это еще с GCC 6.1 (весна 2016-го), а у меня сейчас уже 7.2, и это релизная версия, а не из гита. Так что как бы ты не выкручивался, на данный момент дефолт для Linux это С++14.

anonymous
()
Ответ на: комментарий от eao197

Ну и хорошо, получишь ошибку компиляции о неожиданном типе

И пойдешь писать еще одну копипастнутую лямбду, а на третий раз разродишься темплейтом. И что длиннее будет в итоге?

В ваш же макрос хоть char* засунуть можно и ничего, не подавится.

Всегда считал, что крестовики не в состоянии осилить арифметику над указателями.

$ gcc -Wall lor.c 
lor.c: In function ‘main’:
lor.c:5:29: error: invalid operands to binary % (have ‘char *’ and ‘int’)
 #define do_foo(var) if (var % 2 == 0) ++var
                             ^
lor.c:6:5: note: in expansion of macro ‘do_foo’
     do_foo(x);
     ^
Шпаргалка:
ptr + int;
ptr - int;
ptr - ptr;
Остальное низя.

kawaii_neko ★★★★
()
Ответ на: комментарий от anonymous

Так что как бы ты не выкручивался, на данный момент дефолт для Linux это С++14.

$ gcc --version
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.3 LTS"

Не говоря уже о centos и прочих RHEL-ах. Что там стоит у красноглазых адептов локалхоста, серьезный бизнес совершенно не интересует.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

Не говоря уже о centos и прочих RHEL-ах. Что там стоит у красноглазых адептов локалхоста, серьезный бизнес совершенно не интересует.

LOL, а что ты вообще знаешь про «серьезный бизнес»? Если это софт, которого нет в дистрибутиве (а нового софта в старых репах ес-но нет, не говоря уж про проприетарный софт), то это обычно бинарник, собранный самими разработчиками. Никто не скажет клиенту, что ну ты возьми такой-то компилятор и компиляй. И тем более ему не предложат руками вводить опции для компилятора.

anonymous
()
Ответ на: комментарий от kawaii_neko

И пойдешь писать еще одну копипастнутую лямбду

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

а на третий раз разродишься темплейтом.

Может быть и на второй, но со static_assert внутри.

И что длиннее будет в итоге?

C++ный вариант. Но это плата за безопасность.

Всегда считал, что крестовики не в состоянии осилить арифметику над указателями.

Нет, здесь речь о том, что у kawaii_neko ни ума, ни фантазии, ни чувства юмора.

eao197 ★★★★★
()
Ответ на: комментарий от kawaii_neko

Не говоря уже о centos и прочих RHEL-ах.

Для CentOS/RHEL есть 6.3.1 в изкоробочном практически devtoolset-6, поддерживаемом RH. Субъективно правда интерфейс scl настолько неудобен, что проще бэкпортировать clang из федоры и обновлять самому.

d_a ★★★★★
()
Ответ на: комментарий от d_a

Для CentOS/RHEL есть 6.3.1 в изкоробочном практически devtoolset-6, поддерживаемом RH

А как ты будешь распространять свой софт, слинкованный со свеженькой libstdc++? Без добавления соответствующих репозиториев yum ничего по зависимостям не подхватит.

kawaii_neko ★★★★
()
Ответ на: комментарий от kawaii_neko

А как ты будешь распространять свой софт, слинкованный со свеженькой libstdc++? Без добавления соответствующих репозиториев yum ничего по зависимостям не подхватит.

У меня и со штатной всё запускается. А не должно было?

d_a ★★★★★
()
Ответ на: комментарий от kawaii_neko

Ты бы выяснил сначала обстановку, прежде чем кукарекать.

https://stackoverflow.com/a/15602073

Statically linking libstdc++ would work around the problem, but you don't need to, because that's what the Red Hat Developer Toolset (devtoolset) does for you anyway. The whole point of devtoolset is that it allows you to use a newer GCC and newer libstdc++ but without creating any run-time dependencies on the newer libstdc++ library. The compiled executables only need the system version of libstdc++.so that is always present on RHEL/CentOS, even systems without devtoolset installed.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.