Get rid of nested for loops with std::ranges - c++

Let I have a code:
for (auto& a : x.as)
{
for (auto& b : a.bs)
{
for (auto& c : b.cs)
{
for (auto& d : c.ds)
{
if (d.e == ..)
{
return ...
}
}
}
}
}
as, bs, cs, ds - std::vector of corresponding elements.
Is it possible with std::ranges to convert four ugly loops into a beatifull one line expression?

With join and transform views, you might do:
for (auto& e : x.as | std::views::transform(&A::bs) | std::views::join
| std::views::transform(&B::cs) | std::views::join
| std::views::transform(&C::ds) | std::views::join
| std::views::transform(&D::e))
{
// ...
}
Demo

Related

std::vector of pairs not being sorted properly

I'm trying to sort a vector of pairs by their second value. I'm doing this in order to sort an unordered map, which I'm doing by converting the map to a vector, and then sorting the vector. For some reason, a few values in my vector are not in their right place. Here is my current sort function:
template<typename T1, typename T2>
void sortMapByValue(std::unordered_map<T1, T2> &m) {
std::vector<std::pair<T1, T2>> vec = std::vector<std::pair<T1, T2>>(m.begin(), m.end());
std::sort(vec.begin(), vec.end(),
[](std::pair<T1, T2> a, std::pair<T1, T2> b) { return a.second < b.second; }
);
m = std::unordered_map<std::string, int>(vec.begin(), vec.end());
}
Here is my main function:
int main() {
std::unordered_map<std::string, int> mm;
for (int i = 0; i < 26; i++) {
mm["key" + std::to_string(i)] = i;
}
sortMapByValue<std::string, int>(mm);
for (auto p : mm) {
std::cout << p.first << " | " << p.second << std::endl;
}
}
And here is my output:
key24 | 24
key23 | 23
key22 | 22
key21 | 21
key19 | 19
key17 | 17
key18 | 18
key15 | 15
key14 | 14
key12 | 12
key10 | 10
key25 | 25
key16 | 16
key9 | 9
key7 | 7
key20 | 20
key5 | 5
key4 | 4
key6 | 6
key3 | 3
key8 | 8
key2 | 2
key13 | 13
key1 | 1
key11 | 11
key0 | 0
I'm trying to sort my map because it will be storing the occurrences of a word inside of a file in descending order.
Edit: I have attempted with both a map and an unordered map, and it still has elements in the wrong place.
You cannot do this.
Considering you are using std::unordered_map, as the name implies, it stores everything out of order.
std::map would be of little use either. It uses a binary tree as backing data structure and uses each node's key to determine the position into which it will be placed. If you replace std::unordered_map for it and run your program, you will see that entries are sorted in lexicographic order, as the keys in your map are std::strings.
Depending on what you want to do, you may implement a reverse map or deal with the vector representation when you need sorted entries.

Tweaking clang-format for C++20 ranges pipelines

C++20 (and 23 with std::ranges::to<T>()) makes idiomatic the use of operator| to make a pipeline of transformations such as this:
return numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::ranges::to<std::vector>();
With my project's current .clang-format, that looks something like
return numbers | std::views::filter([](int n) { return n % 2 == 0; }) |
std::views::transform([](int n) { return n * 2; }) | std::ranges::to<std::vector>();
which I find pretty hard to read. If I set BreakBeforeBinaryOperators: All I get
return numbers | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; }) | std::ranges::to<std::vector>();
which is better, but I'd really like the original version with one pipeline operation on each line.
I can adjust the column limit, but that is a major change and also starts to line-break my lambdas, which I don't like:
return numbers | std::views::filter([](int n) {
return n % 2 == 0;
})
| std::views::transform(
[](int n) { return n * 2; })
| std::ranges::to<std::vector>();
I can manually use empty comments to force a newline:
return numbers //
| std::views::filter([](int n) { return n % 2 == 0; }) //
| std::views::transform([](int n) { return n * 2; }) //
| std::ranges::to<std::vector>();
but again, not ideal knowing that pipelines will be pretty common. Am I missing settings? Or is this more of a feature request I should direct to clang-format, like "Add an option so when more than n operator| appears in an expression, put each subexpression on its own line."
There's a feature request for AllowBreakingBinaryOperators. Before the feature completes, only compromise can be made.
As you've said, use // comments to force line breaks.
use clang-format off/on to disable clang-format and format it yourself.
Here's a more complex solution which combines both:
void function() {
return numbers | std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::views::take(3) | std::ranges::to<std::vector>();
}
First, use // to split and then clang-format the code.
void function() {
return numbers
//
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::views::take(3)
//
| std::ranges::to<std::vector>();
}
Next, remove //, use clang-format off/on to disable clang-format.
void function() {
// clang-format off
return numbers
| std::views::filter([](int n) { return n % 2 == 0; })
| std::views::transform([](int n) { return n * 2; })
| std::views::take(3)
| std::ranges::to<std::vector>();
// clang-format on
}
As for matrix, the option AlignArrayOfStructures might help.

ANTLR - How to extact units from a dimension

I'm using ANTLR4 and the CSS grammar from https://github.com/antlr/grammars-v4/tree/master/css3. The grammar defines the following ( pared down a little for brevity )
dimension
: ( Plus | Minus )? Dimension
;
fragment FontRelative
: Number E M
| Number E X
| Number C H
| Number R E M
;
fragment AbsLength
: Number P X
| Number C M
| Number M M
| Number I N
| Number P T
| Number P C
| Number Q
;
fragment Angle
: Number D E G
| Number R A D
| Number G R A D
| Number T U R N
;
fragment Length
: AbsLength
| FontRelative
;
Dimension
: Length
| Angle
;
The matching works fine but I don't see an obvious way to extract the units. The parser creates a DimensionContext which has 3 TerminalNode members - Dimension, Plus and Minus. I'd like to be able to extract the unit during parse without having to do additional string parsing.
I know that one issue that the Length and Angle are fragments. I changed the grammar not use fragments
Unit
: 'em'
| 'ex'
| 'ch'
| 'rem'
| 'vw'
| 'vh'
| 'vmin'
| 'vmax'
| 'px'
| 'cm'
| 'mm'
| 'in'
| 'pt'
| 'q'
| 'deg'
| 'rad'
| 'grad'
| 'turn'
| 'ms'
| 's'
| 'hz'
| 'khz'
;
Dimension : Number Unit;
And things still parse but I don't get any more context about what the units are - the Dimension is still a single TerminalNode. Is there a way to deal with this without having to pull apart the full token string?
You will want to do as little as possible in the lexer:
NUMBER
: Dash? Dot Digit+ { atNumber(); }
| Dash? Digit+ ( Dot Digit* )? { atNumber(); }
;
UNIT
: { aftNumber() }?
( 'px' | 'cm' | 'mm' | 'in'
| 'pt' | 'pc' | 'em' | 'ex'
| 'deg' | 'rad' | 'grad' | '%'
| 'ms' | 's' | 'hz' | 'khz'
)
;
The trick is to produce the NUMBER and UNIT as separate tokens, yet limited to the required ordering. The actions in the NUMBER rule just set a flag and the UNIT predicate ensures that a UNIT can only follow a NUMBER:
protected void atNumber() {
_number = true;
}
protected boolean aftNumber() {
if (_number && Character.isWhitespace(_input.LA(1))) return false;
if (!_number) return false;
_number = false;
return true;
}
The parser rule is trivial, but preserves the detail required:
number
: NUMBER UNIT?
;
Use a tree-walk, parse the NUMBER to a Double and an enum (or equivalent) to provide the semantic UNIT characterization:
public enum Unit {
CM("cm", true, true), // 1cm = 96px/2.54
MM("mm", true, true),
IN("in", true, true), // 1in = 2.54cm = 96px
PX("px", true, true), // 1px = 1/96th
PT("pt", true, true), // 1pt = 1/72th
EM("em", false, true), // element font size
REM("rem", false, true), // root element font size
EX("ex", true, true), // element font x-height
CAP("cap", true, true), // element font nominal capital letters height
PER("%", false, true),
DEG("deg", true, false),
RAD("rad", true, false),
GRAD("grad", true, false),
MS("ms", true, false),
S("s", true, false),
HZ("hz", true, false),
KHZ("khz", true, false),
NONE(Strings.EMPTY, true, false), // 'no unit specified'
INVALID(Strings.UNKNOWN, true, false);
public final String symbol;
public final boolean abs;
public final boolean len;
private Unit(String symbol, boolean abs, boolean len) {
this.symbol = symbol;
this.abs = abs;
this.len = len;
}
public boolean isAbsolute() { return abs; }
public boolean isLengthUnit() { return len; }
// call from the visitor to resolve from `UNIT` to Unit
public static Unit find(TerminalNode node) {
if (node == null) return NONE;
for (Unit unit : values()) {
if (unit.symbol.equalsIgnoreCase(node.getText())) return unit;
}
return INVALID;
}
#Override
public String toString() {
return symbol;
}
}

range-v3: Joining piped ranges with a delimeter

I'm trying to build a basic demo of the range-v3 library: take some integers, filter out odd values, stringify them, then join those into a comma-separated list. For example, { 8, 6, 7, 5, 3, 0, 9 } becomes "8, 6, 0". From reading the docs and going through examples, it seems like the naïve solution would resemble:
string demo(const vector<int>& v)
{
return v |
ranges::view::filter([](int i) { return i % 2 == 0; }) |
ranges::view::transform([](int i) { return to_string(i); }) |
ranges::view::join(", ");
}
but building on Clang 7 fails with a static assertion that one, "Cannot get a view of a temporary container". Since I'm collecting the result into a string, I can use the eager version - action::join - instead:
string demo(const vector<int>& v)
{
return v |
ranges::view::filter([](int i) { return i % 2 == 0; }) |
ranges::view::transform([](int i) { return to_string(i); }) |
ranges::action::join;
}
but the eager version doesn't seem to have an overload that takes a delimiter.
Interestingly, the original assertion goes away if you collect join's inputs into a container first. The following compiles and runs fine:
string demo(const vector<int>& v)
{
vector<string> strings = v |
ranges::view::filter([](int i) { return i % 2 == 0; }) |
ranges::view::transform([](int i) { return to_string(i); });
return strings | ranges::view::join(", ");
}
but this totally defeats the principle of lazy evaluation that drives so much of the library.
Why is the first example failing? If it's not feasible, can action::join be given a delimiter?
action::join should accept a delimiter. Feel free to file a feature request. The actions need a lot of love.

How do I define Context for DataTables in specs2

I'm trying to define some context so that it's executed for each row of data table (before assertion is run on each row).
I've found this example but for the life of me I can't figure out how to write the full test suite.
I'd like to define context once and share it with all examples.
Here is roughly what I have:
class SomeSuite extends Specification with DataTables {
// TODO: define context somehow???
// val context = new Before { println("BEFORE") }
"test 1" should {
"do something" in {
context |
"col1" | "col2" |
val1 ! val2 |
val3 ! val4 |> {
(a, b) => //some assertion with (a, b)
}
}
}
}
I'd like to see "BEFORE" printed each time (total 2 times) before each assertion with (a, b).
I would really appreciate any help.
Thanks ;)
Thanks to Eric here is my final code. I only added 'implicit' since context is shared for many tests:
class SomeSuite extends Specification with DataTables {
implicit val context = new Before { def before = println("BEFORE") }
"test 1" should {
"do something" in {
"col1" | "col2" |
val1 ! val2 |
val3 ! val4 |> { (a, b) =>
a must_== b // this is wrapped with context
}
}
}
}
The easy way is to use the apply method of a Context
class SomeSuite extends Specification with DataTables {
val context = new Before { def before = println("BEFORE") }
"test 1" should {
"do something" in {
"col1" | "col2" |
val1 ! val2 |
val3 ! val4 |> { (a, b) =>
context { a must_== b }
}
}
}
}