0

I was inspired by answer of @fredoverflow from this question : Multiple Counter Problem In For Loop

So I have bunch of variables and bunch of values for them, that I would to pass throw functions. For example:

 bool a, b, c;
 string string_a, string_b, string_c;
 a = foo(string_a);
 b = foo(string_b);
 c = foo(string_c);

Instead of writing this all manually, can I create for loop in a way like this:

for (struct {bool boo; string str;} loop: {{a, string_a}, {b, string_b}, {c, string_c}}){
    loop.boo = foo(loop.str);
}

It does not looks like legit way for variable assignment, or I missed a couple of symbols here? And is such tricks considered a good practice?

2 Answers2

1

std::transform may be helpful.

std::vector<string> src = {string_a, string_b, string_c};
std::vector<bool> dest(src.size());

std::transform(src.begin(), src.end(), dest.begin(), foo);

To have it modify the variables, you can do like this with std::for_each:

std::vector<std::pair<bool*, string*> > targets = {{&a, &string_a}, {&b, &string_b}, {&c, &string_c}};
std::for_each(targets.begin(), targets.end(), [](auto& p){ *p.first = foo(*p.second); });

or with for statement:

for(auto loop: std::vector<std::pair<bool*, string*> > {{&a, &string_a}, {&b, &string_b}, {&c, &string_c}}) {
    *loop.first = foo(*loop.second);
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
0

Combining structured bindings with range based for loops with (C++20) initialization statements

As of C++20, you can use range-based for loop initialization statements, which you can combine with structured bindings as follows:

#include <ios>
#include <iostream>
#include <string>
#include <string_view>
#include <utility>

bool foo(std::string_view s) {
    return s == "b";  
}

int main() {
    bool a{false}, b{false}, c{false};
    std::string string_a{"a"}, string_b{"b"}, string_c{"c"};
    
    for (typedef std::pair<bool&, std::string_view> P;
         auto [res, str] : {P{a, string_a}, {b, string_b}, {c, string_c}}) {
        res = foo(str);
    }
    
    std::cout << std::boolalpha << a << " " << b << " " << c << "\n";
        // false true false
}

leveraging the fact that typedef declarations are init-statements (where alias-declarations are not) to declare the P utility alias, which is in turn used to type deduction in the initializer list over which the range based for loop iterates (and binds values).

Prior to C++20, you could simply place the utility type alias before the loop:

using P = std::pair<bool&, std::string_view>;
for (auto [res, str] : {P{a, string_a}, {b, string_b}, {c, string_c}}) {
    res = foo(str);
}

or remove the utility type alias entirely:

for (auto [res, str] : {
        std::pair<bool&, std::string_view>{a, string_a},
        {b, string_b}, {c, string_c}}) {
    res = foo(str);
}

And is such tricks considered a good practice?

This part of the answer may go into the opinion based domain, and whilst this not not necessarily apply to the snippet above, it surely does so to technique used in the answer you refer to ("Hackety hack hack"). Syntactic compositions are so clever they end up being referred to as "tricks" may end up being be too clever for their own good, and inhibit clarity and understanding of the code base, for the writer of the particular code as well as for other (current and future) maintainers.

dfrib
  • 70,367
  • 12
  • 127
  • 192