From 7cfb1d7961fa73eac8f2cd51598c8ec9229f429b Mon Sep 17 00:00:00 2001 From: Andrew Pardoe Date: Tue, 26 Dec 2017 08:13:24 -0800 Subject: [PATCH 01/15] update date --- CppCoreGuidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 73e4afa..8020056 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -December 11, 2017 +December 26, 2017 Editors: From 7e5019378ba3b90496ccf2f8e15710051f5ed271 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 10:40:45 -0500 Subject: [PATCH 02/15] Fix #493 array myth Added comment about performance to SL.con.1 --- CppCoreGuidelines.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 8020056..3b66251 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -December 26, 2017 +Janualy 1, 2018 Editors: @@ -18558,6 +18558,13 @@ For a variable-length array, use `std::vector`, which additionally can change it Use `gsl::span` for non-owning references into a container. +##### Note + +Comparing the performance of a fixed-sized array allocated on the stack against a `vector` with its elements on the free store is bogus. +You could just as well compare a `std::array` on the stack against the result of a `malloc()` accessed through a pointer. +For most code, even the difference between stack allocation and free-store allocation doesn't matter, but the convenieance and safety of `vector` does. +People working with code for which that difference matters are quite capable of choosing between `array` and `vector`. + ##### Enforcement * Flag declaration of a C array inside a function or class that also declares an STL container (to avoid excessive noisy warnings on legacy non-STL code). To fix: At least change the C array to a `std::array`. From ecabc369bb67a43bddac80e0524727f4f7d89a57 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 11:13:38 -0500 Subject: [PATCH 03/15] simplifying ES.20 #488 --- CppCoreGuidelines.md | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 3b66251..1e2b827 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -10049,11 +10049,6 @@ This cannot trivially be rewritten to initialize `i` and `j` with initializers. Note that for types with a default constructor, attempting to postpone initialization simply leads to a default initialization followed by an assignment. A popular reason for such examples is "efficiency", but a compiler that can detect whether we made a used-before-set error can also eliminate any redundant double initialization. -At the cost of repeating `cond` we could write: - - widget i = (cond) ? f1() : f3(); - widget j = (cond) ? f2() : f4(); - Assuming that there is a logical connection between `i` and `j`, that connection should probably be expressed in code: pair make_related_widgets(bool x) @@ -10061,25 +10056,13 @@ Assuming that there is a logical connection between `i` and `j`, that connection return (x) ? {f1(), f2()} : {f3(), f4() }; } - auto init = make_related_widgets(cond); - widget i = init.first; - widget j = init.second; + auto [i, j] = make_related_widgets(cond); // C++17 + +##### Note -Obviously, what we really would like is a construct that initialized n variables from a `tuple`. For example: - - auto [i, j] = make_related_widgets(cond); // C++17, not C++14 - -Today, we might approximate that using `tie()`: - - widget i; // bad: uninitialized variable - widget j; - tie(i, j) = make_related_widgets(cond); - -This may be seen as an example of the *immediately initialize from input* exception below. - -Creating optimal and equivalent code from all of these examples should be well within the capabilities of modern C++ compilers -(but don't make performance claims without measuring; a compiler may very well not generate optimal code for every example and -there may be language rules preventing some optimization that you would have liked in a particular case). +Complex initialization has been popular with clever programmers for decades. +It has also been a major source of errors and complexity. +Many such errors are introduced during maintenance years after the initial implementation. ##### Example @@ -10104,12 +10087,6 @@ The compiler will flag the uninitialized `cm3` because it is a `const`, but it w Usually, a rare spurious member initialization is worth the absence of errors from lack of initialization and often an optimizer can eliminate a redundant initialization (e.g., an initialization that occurs immediately before an assignment). -##### Note - -Complex initialization has been popular with clever programmers for decades. -It has also been a major source of errors and complexity. -Many such errors are introduced during maintenance years after the initial implementation. - ##### Exception If you are declaring an object that is just about to be initialized from input, initializing it would cause a double initialization. From ff42e7bbaf4b9f31b751ae65b4c7ae44c4a9b856 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 11:44:58 -0500 Subject: [PATCH 04/15] closing #547 (now?) we have CP.3 and CP.31. Also CP.mess. Yes we need more work on CP.mess --- CppCoreGuidelines.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 1e2b827..e1abea0 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -13365,6 +13365,7 @@ Making `surface_readings` be `const` (with respect to this function) allow reaso Immutable data can be safely and efficiently shared. No locking is needed: You can't have a data race on a constant. +See also [CP.mess: Message Passing](#SScp-messs) and [CP.31: prefer pass by value](#C#Rconc-data-by-value). ##### Enforcement From 0df2c26e9a04ef9f887a38012d7390beb528e244 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 13:56:34 -0500 Subject: [PATCH 05/15] C.129 #1052 added Jonathan's example --- CppCoreGuidelines.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index e1abea0..eac8399 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -6852,6 +6852,25 @@ Since each implementation derived from its interface as well as its implementati As mentioned, this is just one way to construct a dual hierarchy. +The implementation hierarchy can be used directly, rather than through the abstract interface. + + void work_with_shape(Shape&); + + int user() + { + Impl::Smiley my_smiley{ /* args */ }; // create concrete shape + // ... + my_smiley.some_member(); // use implementation class directly + // ... + work_with_shape(my_smiley); // use implementation through abstract interface + // ... + } + +This can be useful when the implementation class has members that are not offered in the abstract interface +or if direct use of a member offers optimization oppertunities (e.g., if an implementation member function is `final`) + +##### Note + Another (related) technique for separating interface and implementation is [Pimpl](#Ri-pimpl). ##### Note From edbfc3b8ec92a8b9ed39dec95fdc82d9b8b8c9f1 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 14:26:00 -0500 Subject: [PATCH 06/15] Clarifying (I hope) text and example added addressing #980 and #9777 --- CppCoreGuidelines.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index eac8399..209dd50 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -20294,10 +20294,6 @@ When declaring a class use the following order Use the `public` before `protected` before `private` order. -Private types and functions can be placed with private data. - -Avoid multiple blocks of declarations of one access (e.g., `public`) dispersed among blocks of declarations with different access (e.g. `private`). - ##### Example class X { @@ -20309,9 +20305,33 @@ Avoid multiple blocks of declarations of one access (e.g., `public`) dispersed a // implementation details }; -##### Note +##### Example -The use of macros to declare groups of members often violates any ordering rules. +Sometimes, the default order of members conflicts with a desire to separate the public interface from implementation details. +In such cases, private types and functions can be placed with private data. + + class X { + public: + // interface + protected: + // unchecked function for use by derived class implementations + private: + // implementation details (types, functions, and data) + }; + +##### Example, bad + +Avoid multiple blocks of declarations of one access (e.g., `public`) dispersed among blocks of declarations with different access (e.g. `private`). + + class X { // bad + public: + void f(); + public: + int g(); + // ... + }; + +The use of macros to declare groups of members often leads to violation of any ordering rules. However, macros obscures what is being expressed anyway. ##### Enforcement From 72b9b40a7c5cf8d8d083a788681f1c314ddc6594 Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 15:47:20 -0500 Subject: [PATCH 07/15] Some examples in Per.11 In response to #936 I added some text to Per.11. More would be welcome --- CppCoreGuidelines.md | 77 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 209dd50..296bcff 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -13094,7 +13094,82 @@ Type violations, weak types (e.g. `void*`s), and low-level code (e.g., manipulat ### Per.11: Move computation from run time to compile time -??? +##### Reason + +To decrease code size and run time. +To avoid data races by using constants. +To catch errors at compiler time (and thus eliminate the need for error-handling code). + +##### Example + + double square(double d) { return d*d; } + static double s2 = square(2); // old-style: dynamic initialization + + constexpr double ntimes(double d, int n) // assume 0<=n> + { + double m = 1; + while (n--) m*=d; + return m; + } + constexpr double s3 {ntimes(2,3)}; // modern-style: compile-time initialization + +Code like the initialization of `s2` isn't uncommon, especially for initialization that's a bit more complicated than `square()`. +However, compared to the initialization of `s3` there are two problems: + +* we suffer the overhead of a function call at run time +* `s2` just might be accessed by another thread before the initialization happens. + +Note: you can't have a data race on a constant. + +##### Example + +Consider a popular technique for providing a handle for storing small objects in the handle itself and larger ones on the heap. + + constexpr int on_stack_max = 20; + + template + struct Scoped { // store a T in Scoped + // ... + T obj; + }; + + template + struct On_heap { // store a T in on the free store + // ... + T* objp; + }; + + template + using Handle = typename std::conditional<(sizeof(T)<=on_stack_max), + Scoped, // first alternative + On_heap // second alternative + >::type; + + void f() + { + Handle v1; // the double goes on the stack + Handle> v2; // the array goes on the free store + // ... + } + +Assume that `Scoped` and `On_heap` provide compatible user interfaces. +Here we compute the optimal type to use at compile time. +There are similar techniques for selecting the optimal function to call. + +##### Note + +The ideal is {not} to try execute everything at compile time. +Obviously, most computations depend on inputs so they can't be moved to compile time, +but beyond that logical constraint is the fact that complex compile-time computation can seriously increase compile times +and complicate debugging. +It is even possible to slow down code by compile-time computation. +This is admittedly rare, but by factoring out a general computation into separate optimal sub-calculations it is possible to render the instruction cache less effective. + +##### Enforcement + +* Look for simple functions that might be constexpr (but are not). +* Look for functions called with all constant-expression arguments. +* Look for macros that could be constexpr. ### Per.12: Eliminate redundant aliases From 8f87a035880e988307f023af5d54139b07416a9d Mon Sep 17 00:00:00 2001 From: Bjarne Stroustrup Date: Mon, 1 Jan 2018 17:37:38 -0500 Subject: [PATCH 08/15] ES.87 #1032 comment and example added. To consider: should integer comparison be broken out as its own rule? --- CppCoreGuidelines.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 296bcff..4f71d8c 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -12430,6 +12430,23 @@ For example: This invokes `istream`'s `operator bool()`. +##### Note + +Explicit comparison of an integer to `0` is in general not redundant. +The reason is that (as opposed to pointers and Booleans) an integer often have more than two resonable values. +Furthermore `0` (zero) is often used to indicate success. +Consequently, it is best to be specific about the comparison. + + void f(int i) + { + if (i) // suspect + // ... + if (i==success) // possibly better + // ... + } + +Always remember that an integer can have more that two values. + ##### Example, bad It has been noted that @@ -12442,7 +12459,7 @@ Being verbose and writing if(strcmp(p1, p2) != 0) { ... } // are the two C-style strings equal? (mistake!) -would not save you. +would not in itself save you. ##### Note From 7dc8aaa420848a26df92e93dd05e01b2b661db10 Mon Sep 17 00:00:00 2001 From: Sergey Zubkov Date: Tue, 2 Jan 2018 22:14:16 -0500 Subject: [PATCH 09/15] travis CI fixes --- scripts/hunspell/isocpp.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/hunspell/isocpp.dic b/scripts/hunspell/isocpp.dic index f59d1bb..838c740 100644 --- a/scripts/hunspell/isocpp.dic +++ b/scripts/hunspell/isocpp.dic @@ -468,6 +468,7 @@ RVO s1 s1's s2 +s3 Sarkar scanf Sd From 59eb77027ee8ac3b71eda551f30278beed59d7ae Mon Sep 17 00:00:00 2001 From: Sergey Zubkov Date: Tue, 2 Jan 2018 22:15:02 -0500 Subject: [PATCH 10/15] travis CI fixes --- CppCoreGuidelines.md | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 4f71d8c..c6fbcad 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -Janualy 1, 2018 +January 1, 2018 Editors: @@ -6867,7 +6867,7 @@ The implementation hierarchy can be used directly, rather than through the abstr } This can be useful when the implementation class has members that are not offered in the abstract interface -or if direct use of a member offers optimization oppertunities (e.g., if an implementation member function is `final`) +or if direct use of a member offers optimization opportunities (e.g., if an implementation member function is `final`) ##### Note @@ -10076,7 +10076,7 @@ Assuming that there is a logical connection between `i` and `j`, that connection } auto [i, j] = make_related_widgets(cond); // C++17 - + ##### Note Complex initialization has been popular with clever programmers for decades. @@ -12433,15 +12433,15 @@ This invokes `istream`'s `operator bool()`. ##### Note Explicit comparison of an integer to `0` is in general not redundant. -The reason is that (as opposed to pointers and Booleans) an integer often have more than two resonable values. +The reason is that (as opposed to pointers and Booleans) an integer often has more than two reasonable values. Furthermore `0` (zero) is often used to indicate success. Consequently, it is best to be specific about the comparison. void f(int i) { - if (i) // suspect + if (i) // suspect // ... - if (i==success) // possibly better + if (i == success) // possibly better // ... } @@ -13115,20 +13115,20 @@ Type violations, weak types (e.g. `void*`s), and low-level code (e.g., manipulat To decrease code size and run time. To avoid data races by using constants. -To catch errors at compiler time (and thus eliminate the need for error-handling code). +To catch errors at compile time (and thus eliminate the need for error-handling code). ##### Example double square(double d) { return d*d; } static double s2 = square(2); // old-style: dynamic initialization - constexpr double ntimes(double d, int n) // assume 0<=n> + constexpr double ntimes(double d, int n) // assume 0 <= n { double m = 1; - while (n--) m*=d; + while (n--) m *= d; return m; } - constexpr double s3 {ntimes(2,3)}; // modern-style: compile-time initialization + constexpr double s3 {ntimes(2, 3)}; // modern-style: compile-time initialization Code like the initialization of `s2` isn't uncommon, especially for initialization that's a bit more complicated than `square()`. However, compared to the initialization of `s3` there are two problems: @@ -13148,25 +13148,25 @@ Consider a popular technique for providing a handle for storing small objects in struct Scoped { // store a T in Scoped // ... T obj; - }; + }; template - struct On_heap { // store a T in on the free store + struct On_heap { // store a T on the free store // ... T* objp; - }; + }; template - using Handle = typename std::conditional<(sizeof(T)<=on_stack_max), - Scoped, // first alternative - On_heap // second alternative - >::type; + using Handle = typename std::conditional<(sizeof(T) <= on_stack_max), + Scoped, // first alternative + On_heap // second alternative + >::type; void f() { - Handle v1; // the double goes on the stack - Handle> v2; // the array goes on the free store - // ... + Handle v1; // the double goes on the stack + Handle> v2; // the array goes on the free store + // ... } Assume that `Scoped` and `On_heap` provide compatible user interfaces. @@ -13476,7 +13476,7 @@ Making `surface_readings` be `const` (with respect to this function) allow reaso Immutable data can be safely and efficiently shared. No locking is needed: You can't have a data race on a constant. -See also [CP.mess: Message Passing](#SScp-messs) and [CP.31: prefer pass by value](#C#Rconc-data-by-value). +See also [CP.mess: Message Passing](#SScp-mess) and [CP.31: prefer pass by value](#C#Rconc-data-by-value). ##### Enforcement @@ -18651,7 +18651,7 @@ Use `gsl::span` for non-owning references into a container. Comparing the performance of a fixed-sized array allocated on the stack against a `vector` with its elements on the free store is bogus. You could just as well compare a `std::array` on the stack against the result of a `malloc()` accessed through a pointer. -For most code, even the difference between stack allocation and free-store allocation doesn't matter, but the convenieance and safety of `vector` does. +For most code, even the difference between stack allocation and free-store allocation doesn't matter, but the convenience and safety of `vector` does. People working with code for which that difference matters are quite capable of choosing between `array` and `vector`. ##### Enforcement From e1cc6fedb03fb4a58fa7d106dd8a02712d704416 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Mon, 8 Jan 2018 13:53:39 -0500 Subject: [PATCH 11/15] In docs\gsl-intro, cleaned up stray notes into a "hints" section --- docs/gsl-intro.md | 51 ++++++----------------------------------------- 1 file changed, 6 insertions(+), 45 deletions(-) diff --git a/docs/gsl-intro.md b/docs/gsl-intro.md index 25213b6..c33ec6f 100644 --- a/docs/gsl-intro.md +++ b/docs/gsl-intro.md @@ -3,7 +3,7 @@ by Herb Sutter -updated 2017-05-24 +updated 2018-01-08 ## Overview: "Is this document a tutorial or a FAQ?" @@ -277,51 +277,12 @@ Also, `span` lets you distinguish between `.size()` and `.size_bytes()`; make > - Prefer `span`'s `.size_bytes()` instead of `.size() * sizeof(T)`. +## And a few `span`-related hints -


-# *** TODO - Other span suggestions and questions back to Bjarne and Neil +These are not directly related to `span` but can often come up while using `span`. -Bjarne suggested: + * Use `byte` everywhere you are handling memory (as opposed to characters or integers). That is, when accessing a chunk of raw memory, use `gsl::span`. -- given an STL style interface ([b:e)), how do I implement it using a span? - -HS: I couldn't think of an example so I skipped this - -- show a use of string_span - -HS: I think we're dropping this, so it doesn't need an example, right? - -- I would concentrate on span and push not_null(), narrow(), and friends to a separate note. - -HS: OK, stopping with the above for now -- what more can we say about span? - -- I would be happy to review a rough draft. - -HS: Here you go! :) - -Neil suggested: - -- some guidance on how to deal with standard lib container size_t vs span ptrdiff_t mismatch. - -HS: Do you have an example in mind? - - -




-# MORE RAW NOTES - -I'll continue with more of these, and possibly in a separate note as Bjarne suggests a few lines above, if everyone agrees. - -## Neil - -- use `byte` everywhere you are handling memory (as opposed to characters or integers) - -- use `narrow()` when you cannot afford to be surprised by a value change during conversion to a smaller range (includes going between signed to unsigned) - -- use `narrow_cast()` when you are *sure* you won’t be surprised by a value change during conversion to a smaller range - -> - pass `not_null` by value - -I suspect this isn't right -- I think it should be "pass `not_null` the same as `T`". For example, `not_null` should be passed by value, but `not_null>` should probably be passed by `const&`. - -- use `not_null` on any raw pointer parameter that should never contain nullptr + * Use `narrow()` when you cannot afford to be surprised by a value change during conversion to a smaller range. This includes going between a signed `span` size or index and an unsigned today's-STL-container `.size()`, though the `span` constructors from containers nicely encapsulate many of these conversions. + * Similarly, use `narrow_cast()` when you are *sure* you won’t be surprised by a value change during conversion to a smaller range From 7aac5313d9b0e288d33cc811c3b31378fde87b23 Mon Sep 17 00:00:00 2001 From: Herb Sutter Date: Tue, 9 Jan 2018 11:02:25 -0500 Subject: [PATCH 12/15] Correct example comment --- docs/gsl-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/gsl-intro.md b/docs/gsl-intro.md index c33ec6f..9187220 100644 --- a/docs/gsl-intro.md +++ b/docs/gsl-intro.md @@ -59,7 +59,7 @@ dangerous_process_ints(&*remainder, v.end() - remainder); // correct but convolu Instead, using `span` encapsulates the pointer and the length: ~~~cpp -// BETTER: Read n contiguous ints starting at *p +// BETTER: Read s.size() contiguous ints starting at s[0] void process_ints(span s); ~~~ From 5c3006b60f6af3c3a40020f84c0107a2147b5d88 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Tue, 9 Jan 2018 22:27:14 +0000 Subject: [PATCH 13/15] Change "untended" to "unintended" in C.9 --- CppCoreGuidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index c6fbcad..d79642b 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -4180,7 +4180,7 @@ Flag classes declared with `struct` if there is a `private` or `protected` membe Encapsulation. Information hiding. -Minimize the chance of untended access. +Minimize the chance of unintended access. This simplifies maintenance. ##### Example From 999f9dd0ceb066be31d8ca19d45532155c95834e Mon Sep 17 00:00:00 2001 From: Andrew Pardoe Date: Mon, 22 Jan 2018 10:39:01 -0800 Subject: [PATCH 14/15] Adding "ptrdiff" to travis dictionary --- scripts/hunspell/isocpp.dic | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/hunspell/isocpp.dic b/scripts/hunspell/isocpp.dic index 838c740..721e4ba 100644 --- a/scripts/hunspell/isocpp.dic +++ b/scripts/hunspell/isocpp.dic @@ -407,6 +407,7 @@ Productinfo proto ps ptr +ptrdiff Ptr ptr2 ptr's From 9de66ec0274f608fab2213e7c450a33d5feb1c39 Mon Sep 17 00:00:00 2001 From: Alexey Porotnikov Date: Mon, 22 Jan 2018 20:43:41 +0200 Subject: [PATCH 15/15] add "return" to the compound literal exclusions (#1113) inhibits readability/brace warning for compound literals in return statement --- scripts/python/cpplint.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/python/cpplint.py b/scripts/python/cpplint.py index 95c0c32..825c87c 100755 --- a/scripts/python/cpplint.py +++ b/scripts/python/cpplint.py @@ -4091,6 +4091,7 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error): (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or Search(r'\bdecltype$', line_prefix) or + Search(r'\breturn\s*$', line_prefix) or Search(r'\s+=\s*$', line_prefix)): match = None if (match and