C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Manifold comparison operator

From: Ben Crowhurst <ben.crowhurst_at_[hidden]>
Date: Sat, 09 Sep 2023 14:22:53 +0000
This and a number of alternative solutions exist, yet all detract from the codes readability.

The result is every project having its own version of hamcrest.

Sent from Proton Mail mobile

-------- Original Message --------
On 7 Sept 2023, 23:15, Breno GuimarĂ£es wrote:

> It should be possible to write a template that looks more readable:
>
> if (matcher(var).is_none_of(opt1, opt2, opt3))
>
> Was that considered?
>
> Best regards,
> Breno G
>
> Em qui., 7 de set. de 2023 18:56, Ben Crowhurst via Std-Proposals <std-proposals_at_[hidden]> escreveu:
>
>> We propose a new range of conditional operators for C++:
>>
>> * [^] one-of
>> * [&] all-of
>> * [|] any-of
>> * [!] none-of
>>
>> These operators simplify common code patterns and improve the clarity of basic conditional expressions.
>>
>> 1 ) Before/After
>> Sample code is taken from the llvm-project (clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp).
>>
>> Before the proposal:
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
>> OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
>> OOK == OO_Spaceship))
>> return false;
>>
>> After the proposal:
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (OOK [!] OO_EqualEqual, OO_ExclaimEqual, OO_Less, OO_LessEqual, OO_Greater,
>> OO_GreaterEqual, OO_Spaceship)
>> return false;
>>
>> Resulting in a 21.7% decrease in character count and a reduction in cognitive load, with no requirement to study each comparison operator delimited by subsequent logical operators.
>>
>> 2) Proposal
>> There is one conditional operator in the C++ standard :? (Ternary). We propose to expand this horizon with four additional operators.
>>
>> Operator Statement Equivalence
>>
>> one of if (var [^] val1, val2) E; if ((var == val1 && var != val2) || (var != val1 && var == val2)) E;
>>
>> all of if (var [&] val1, val2) E; if (var == val1 && var == val2) E;
>>
>> any of if (var [|] val1, val2) E; if (var == val1 || var == val2) E;
>>
>> none of if (var [!] val1, val2) E; if (var != val1 && var != val2) E;
>>
>> As is the case with the Ternary operator, no overloading will be supported. This ensures that conditional operands are only evaluated based on the truth or falseness of the conditional expression.
>>
>> The Comma operator will NOT be interpreted during the evaluation of the right-hand side of a Manifold operator.
>>
>> 3) Motivation
>> To improve code clarity when interpreting multifaceted conditionals.
>>
>> 4) Alternatives
>> A number of alternative solutions exist, however, all inflict increasing code complexity and distract from interpreting the program control flow.
>>
>> 4.1) Macro solution
>>
>> #define NONE_OF0(lhs, rhs, ...) lhs != rhs
>> #define NONE_OF1(lhs, rhs, ...) lhs != rhs && NONE_OF0(lhs, __VA_ARGS__)
>> // Add NONE_OF2, 3, 4, 5... as desired.
>> #define NONE_OF(lhs, ...) NONE_OF1(lhs, __VA_ARGS__)
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (NONE_OF(OOK, OO_EqualEqual, OO_ExclaimEqual, OO_Less, OO_LessEqual,
>> OO_Greater, OO_GreaterEqual, OO_Spaceship))
>> return false;
>>
>> A macro solution is hard to debug, verbose, specific to the desired number of potential arguments, and fails to clearly communicate the intent; comparison LHS appears as the first argument.
>>
>> 4.2) Template solution
>>
>> template <typename LHS, typename... Args>
>> auto none_of(LHS lhs, Args... args)
>> {
>> const auto predicate = std::not_equal_to<LHS>();
>> return (predicate(lhs, args) && ...);
>> }
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (none_of(OOK, OO_EqualEqual, OO_ExclaimEqual, OO_Less, OO_LessEqual,
>> OO_Greater, OO_GreaterEqual, OO_Spaceship))
>> return false;
>>
>> Much improved over the macro solution, yet fails to clearly communicate the intent without understanding the internals; comparison LHS appears as the first argument.
>>
>> 4.3) STL solution
>> std::all_of, std::any_of, std::none_of and std::ranges equivalent.
>>
>> const std::set<OverloadedOperatorKind> ComparisonOperators = {
>> OO_EqualEqual, OO_ExclaimEqual, OO_Less, OO_LessEqual, OO_Greater,
>> OO_GreaterEqual, OO_Spaceship
>> };
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (std::none_of(std::cbegin(ComparisonOperators),
>> std::cend(ComparisonOperators),
>> [&](auto op) { return op == OOK; }))
>> return false;
>>
>> The STL solution presents a verbose conditional construction and is not easily capable of handling variables, literals, and functional call-sites without increasing code complexity. Additionally, there is no support for std::one_of.
>>
>> 4.4) Helper function solution
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (is_not_comparison_operator(OOK))
>> return false;
>>
>> Provides a clean and concise meaning towards control flow, however, it results in the proliferation of many helper functions to address all possible combinations throughout a codebase e.g. is_comparison_operator, is_equality_operator, is_not_equality_operator, etc.
>>
>> In more involved scenarios this approach becomes unwieldy, as dynamic aspects must be passed into the function as arguments or expansion of the condition must occur i.e.
>>
>> const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
>> if (is_not_comparison_operator(OOK) && OOK != NextToken().getKind())
>> return false;
>>
>> 5) Impact on the Standard
>> The newly proposed syntax is ill-formed in the current working draft. This is a core language extension.
>>
>> 6) Proposed wording
>> Section '7.6 Compound expressions' requires the addition of new grammar and wording detailing the Manifold operator syntax. TBD.
>>
>> 7) Implementation
>>
>> A rudimentary implementation was crafted into the llvm-project to determine the impact on existing compiler infrastructure.
>>
>> * Lex library - Addition of new manifold tokens.
>> * Parse library - Modification of binary expression parsing to emit equivalent entries into the AST.
>>
>> Minimal changes were required.
>>
>> Thank you for your time.
>>
>> Regards,
>> Ben Crowhurst
>>
>> --
>> Std-Proposals mailing list
>> Std-Proposals_at_[hidden]ocpp.org
>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2023-09-09 14:23:15