C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Manifold comparison operator

From: David Brown <david_at_[hidden]>
Date: Fri, 8 Sep 2023 11:43:36 +0200
It should even be possible to write something like :

 if (var <= none_of(opt1, opt2, opt3)) ...

by having "none_of" return a type X for which there is an operator <=
(LHS, X) overload.


But I would not recommend standardising on such a syntax unless people
agreed to use a similar syntax for many new features. And maybe it
would be best to have a new operator specifically for such purposes -
perhaps a two operand ~. That would give us :

 if (var ~ none_of(opt1, opt2, opt3)) ...
 if (var ~ all_of(opt1, opt2, opt3)) ...
 
I think having the words "none_of", "all_of", etc., would be easier to
remember than multiple new operators.

David




On 08/09/2023 00:15, Breno GuimarĂ£es via Std-Proposals 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] <mailto: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] <mailto:Std-Proposals_at_[hidden]>
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> <https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals>
>
>

Received on 2023-09-08 09:43:44