I++ vs i+=1


  • FoxDev

    Well I'll be happy to discuss this, However i feel it important to point out that my initial post that you have quoted turned out to be way more hardass "my way or the high way" than i planned.

    My reasons for the ban are fairly simple:

    • I reject the idea that idiomatically read-and-modify-as-sideffect operators are always a good thing.
    • They can be used for good, yes, but in actual experience 999 time out of 1000 i see them used in a way that obfuscates the underlying reasoning behind the algorithm
    • all my style checks are completely automated. there is a syntax/style checker configuration that everyone has and everyone uses.
    • This means that while they style of the project may not be ideal for an individual developer that developer never has to context switch between styles while working on the code. We all use the same basic style of code so all developers can focus on the meaning of the code not what it says. and the tool enforces that.
    • This also means that code reviews never devolve into a question of style. the proposed checkin follows the style guidelines as set and enforced by our style checker and any style discussions are quickly sidelined for later, dedicated meetings (which rarely actually happen as the developer that calls for the meeting never schedules it)
    • Because the style checks are automated there are no exceptions. I have yet to find a style checker that differentiates i++; as a statement from i++ as an expression.
    • This means I can't allow the pre/post increment statement but disallow the obfuscating pre/post increment expression
    • All sections of the code style checker are open to discussion and will be changed if a compelling argument is made
    • this has happened many times already, so many times that less than 25% of my original style choices are untouched from when i originally set them.
    • All developers under me are encouraged to bring up their concerns with any of the best practices/enforced style checks
    • Because I do not work on Systems programming in C or C++ where operator overload is common I do not have to deal with cases where an operation requires pre/post increment because of operator overloading.
    • Should I ever work in a project where C or C++ are used with operator overloading them of course i would review there guidelines with the team and come up with an alternate set that satisfied both the needs of the product and appropriately curbed all developers desire to be "clever


  • @accalia said:

    Because the style checks are automated there are no exceptions. I have yet to find a style checker that differentiates i++; as a statement from i++ as an expression.

    The ; is a bit of a giveaway, isn't it? 🚎


  • FoxDev

    to a human, yes... for some reason none of the programatic style checkers i know about can tell the difference, and importantly the style check is completely automated.



  • I am now sufficiently intrigued to request further information about the style checkers in question...


  • FoxDev

    for (var i = 0; i < x++; i += 2)
    {
        //Soem coed
    }
    

    Now if you'll excuse me, I have to go perform a purity ritual to rid myself of the stench of that code.

    Disclaimer: I have never written a loop like that outside of this post; if I'm lying, then you may pull every quill from my body. Without anaesthetic.



  • Well, that's just nonsensical. Imagine putting a var declaration inside the for loop. (That is JavaScript, right?)


  • FoxDev

    Could be; could also be C#. But I used the C syntax hint…
    @tar said:

    Imagine putting a var declaration inside the for loop.

    Also, that's a really common thing to do 😛



  • @RaceProUK said:

    Also, that's a really common thing to do

    Not in JS, where variables are function-scoped (as opposed to block-scoped).


  • FoxDev

    @tar said:

    Not in JS, where variables are function-scoped (as opposed to block-scoped).

    …we work on very different codebases…




  • FoxDev

    unless they have added the option since last i checked:

    • eslint
    • pylint
    • mcs
    • jslint
    • resharper
    • jslint
    • jshint
    • phplint
    • pyflakes

    That's not an exhaustive list but it's the ones we use in several projects i'm lead in currently as well as some of the ones we've looked at recentlyish. And yes some of those listed are just lint checkers... just to head off you pedants.

    If you know of an option to allow ++ as a statement while keeping them disallowed as an expression in these or in any other checker that supports Javascript C# or python I welcome hearing about it.


  • FoxDev

    All declarations are hoisted to the top of the scope in which they appear.

    😷

    @accalia said:

    That's not an exhaustive list

    But it does have jslint twice ;)



  • Anything derived from JSLint is liable to be a lost cause. It's one of those things Crockford has a bee in his bonnet about:

    The ++ (increment) and -- (decrement) operators have been known to contribute to bad code by encouraging excessive trickiness. They are second only to faulty architecture in enabling to viruses and other security menaces. Also, preincrement/postincrement confusion can produce off-by-one errors that are extremely difficult to diagnose.


  • FoxDev

    it's a stance i happen to agree with him on, for expressions at least. when used in expressions they introduce side effects and that's never good for code readability and a really good way to venture into nasal demon territory if you are not careful.

    as statements they're meh as far as i'm concerned. i wouldn't mind allowing them as statements



  • That seems fair. I assume that the style checkers are able to eliminate x = i += 1 though. Stupid style checkers. I mean, it's just, I work hard to try and ensure every line I write is human readable*, so the idea of operator++ being banned just because it can be abused really burns, like “why am I (hypothetically) being punished for someone I never did?”. But I suppose not being subjected to ++i=i++ is worth a minor annoyance if there's no alternative.

    <asdf>* that lower case i in the code sample broke my keyboard's brain so every first person pronoun in this post had to be manually capitalized.



  • @flabdablet said:

    That was my exact reaction on first exposure to cout << "Hello world!"

    Gratuitous overload of bit-shift to mean something completely and utterly different was just Wrong From The Start.

    I'm sure you'll love boost::format.

    int x = 2, y = 3;
    
    std::string result = (boost::format("The sum of %d and %d is %d.") % x % y % (x + y)).str();
    

  • FoxDev

    some style checkers are able to catch that error others not. no style checker will be perfect, but i rely on them to catch the most stupid stuff that every developer, no matter how good, occasionally does when they're not paying as close attention as they should, and rely on working with smart and honest people for the rest..

    I also find that having the style enforced by a tool as part of SOP means that a lot of the ego arguments over style are neatly sidestepped. the tool just cares your code passes its set of rules and the team set those rules (i mean it was my ruleset initially but the team has had significant impact on it over the years and will continue to have)



  • Call me a traditionalist, but at the end of the day, automated style checking is nice to have, but I don't think it can replace actual humans looking at the code line-by-line before it's checked in. There's always going to be edge-cases where the tools can't infer what is actually meant, and whether a minor bending of the guidelines actually enhances the readability and maintainability of the code. Another nice benefit of manual review is that programmers get exposed to more code than they likely would if they're left to their own devices, so it helps to share knowledge and reinforce good practise in a way that a computer test just can't.



  • @Groaner said:

    boost::format.

    Presumably the % overload was picked to make the right-hand builder pattern terms resemble the format specifiers to make it clear that they'll get substituted where the specifiers occur. But that clarity only appears if you pay attention to whitespace, like so:

    (boost::format("The sum of %d and %d is %d.") %x %y %(x + y))

    which grates against most developers' natural inclination to surround operators with whitespace.

    It seems to me that overloading the comma would have been a much better design choice, because

    (format("The sum of %d and %d is %d."), x, y, x + y)

    looks rather more natural and doesn't do so much violence to the original meaning of the overloaded operator.

    Commas have usefully low precedence (so no need to parenthesize the x + y) and start out with overloaded meanings anyway (item separators vs. an evaluate-this-then-that operator). Using them this way in a builder pattern could be viewed as a special case of either of those meanings without needing to squint too hard.



  • Commas outside of operator() like that is unparseable.

    Modulo has good precedent as the string interpolation operator. No need to get clever.



  • if (x) is my guilty pleasure.



  • @Buddy said:

    Modulo has good precedent as the string interpolation operator. No need to get clever.

    I always thought that was just one of those weird Python things that Python did just to be weird so that smug Pythonistas could claim that it's perfectly intuitive and like everything that Python does, it's completely the best and most wonderful way to do anything and not just some weird Python nonsense that sucks.

    Filed under: that sentence kind of got away from me there...



  • @Buddy said:

    Commas outside of operator() like that is unparseable

    I am by no means a C++ guy (I find the language utterly repulsive) but I'd be interested in knowing why that is so.

    If format() returns an object whose class defines an operator,(int) method, and x is an int, and I write format(), x then what ambiguity would stop that from being parsed as a method call to the operator,(int) method of the object returned by format()? Why would the use of operator%(int) be any different?

    Also, on the matter of precedent: I agree that the % character has long been used as a format specifier prefix inside format strings (in printf() and strftime() and heaps of others) but where else does % turn up as an overloaded operator in builder patterns?

    Edit: TIL that Python uses % in a similar but not identical way; % is an operator applicable to any string, but the item to the right of the % has to be a list if there's more than one % conversion embedded in the string - this is not a C++ builder pattern.



  • @flabdablet said:

    If <code>format()</code> returns an object whose class defines an <code>operator,(int)</code> method, and <code>x</code> is an int, and I write <code>format(), x</code> then what ambiguity would stop that from being parsed as a method call to the <code>operator,(int)</code> method of the object returned by <code>format()</code>? Why would the use of <code>operator%(int)</code> be any different?

    (...or perhaps it's better to just strip all the formatting completely. Thanks for these two great options Duckhose, you are truly spoiling us...)

    @flabdablet said:

    If format() returns an object whose class defines an operator,(int) method, and x is an int, and I write format(), x then what ambiguity would stop that from being parsed as a method call to the operator,(int) method? Why would the use of operator%(int) be any different?

    This seems feasible, I'm going to see if I can knock up a toy example, and get back to you...

    Filed under: I literally cannot believe that anyone in their right mind would look at Dorkhouse's quoting mechanism and say "yes, this works perfectly as intended, there's no way anyone could say that it's a buggy piece of shit..."



  • @Buddy said:

    if (x) is my guilty pleasure.

    Man, that's so bad...

    [size=7]I've done that too >.>[/size]



  • @accalia said:

    If you know of an option to allow ++ as a statement while keeping them disallowed as an expression in these or in any other checker that supports Javascript C# or python I welcome hearing about it.
    I got bored and started playing with Roslyn, and after recreating the parse tree explorer from the Visual Studio CTP and messing around with a few code nuggets, I have two rules that would work for a Roslyn-based linter (like StyleCop vNext)

    • Pre-/Post-increment statements: Pre/PostIncrementExpression nodes that are direct children of Block nodes.
    • Pre-/Post-increment expressions for for loops: Pre/PostIncrementExpression nodes that are direct children of ForStatement nodes, immediately after the second SemicolonToken.
    • All other Pre/PostIncrementExpressions would print a diagnostic.

    Want me to actually write the code for that?



  • If I have -18446744073709551616 (or -2147483648 for you 32-bit users) and I multiply it by -1, what do you get?



  • @flabdablet said:

    ...an object whose class defines an operator,(int) method...

    @tar said:

    ...toy example...

    Well, it seems feasible at least. I built a concatenator because its easier to do than a formatter, but I think the general idea is sound.

    #include <string>
    #include <stdio.h>
    
    using namespace std;
    
    struct ConcatX {
        ConcatX(string &str): str_(str) {}
    
        operator const string&() {
            return str_;
        }
        ConcatX &operator,(int i) {
            char tmp[100];
            snprintf(tmp, 100, "%i", i);
            str_ = str_ + string(tmp);
            return *this;
        }
        ConcatX &operator,(const string &s) {
            char tmp[100];
            snprintf(tmp, 100, "%s", s.c_str());
            str_ = str_ + string(tmp);
            return *this;
        }
    private:
        string &str_;
    };
    
    struct Concat {
        Concat(const string &str): str_(str) {}
    
        ConcatX start() {
            return ConcatX(str_);
        }
    private:
        string str_;
    };
    
    ConcatX concat(const string &str) {
        return Concat(str).start();
    }
    
    int main() {
        string str = (concat("abcd"), 1, 2, "bob", 4);
        printf("str = %s\n", str.c_str()); // prints 'str = abcd12bob4'
    }
    

    Although bear in mind that this code was written in 10 minutes, and shouldn't serve as an example of how to do anything ever.

    EDIT: replaced Concat::operator() with Concat::start()



  • What's the point of Concat in there? Why not go straight to ConcatX?



  • I was about to ask the same thing. What problem does Concat's operator()() method solve?



  • @flabdablet said:

    What problem does Concat's operator()() method solve?

    Yeah, Concat is basically there to hold the instance of the string which the ConcatX is operating on. There is no good reason on earth for it to have an operator() rather than a function with a sensible name...

    EDIT and in fact, I've changed it - that operator() is now a function called start() instead...



  • It might be possible to combine the two classes into a single one, but, WORKSASDESIGNED_SHIPIT.



  • @flabdablet said:

    >Commas outside of operator() like that is unparseable

    I am by no means a C++ guy (I find the language utterly repulsive) but I'd be interested in knowing why that is so.

    Sorry, I didn't mean that it's mathematically unparseable, I just meant that I personally find it unparseable. My eyes see a line beginning with an identifier and ending with a paren, that's usually enough for me.

    The only ambiguity I can think of is passing a pre- or partially-formatted instance to another function; you'd have to put parens around it like other((format("%d"), 42));

    @flabdablet said:

    where else does % turn up as an overloaded operator in builder patterns?

    The most sensible operator to use for builder patterns is <<. Obviously, people who know binary won't like that, but it is too late now, the die has been cast.

    @flabdablet said:

    Edit: TIL that Python uses %

    And Ruby (thus I presume Perl does too?).

    Anyway, let's take this from the top: arithmetic operators don't really make sense on strings, but in general, the utility of string operators far outweighs any ideological BITCHs against them. If you think about what the common string operations are, and how those could possibly map to operators, I'm pretty sure % encoding is where you'll end up.



  • Integer overflow. Why do you ask?


  • :belt_onion:

    See Also: Nasal Demons

    Filed Under: IIRC. Haven't read the spec recently...



  • @sloosecannon said:

    IIRC
    Signed overflow is, indeed, always undefined behavior. Unsigned "overflow" is defined to be mod 2#bits.



  • @Buddy said:

    The only ambiguity I can think of is passing a pre- or partially-formatted instance to another function; you'd have to put parens around it like other((format("%d"), 42));

    That, actually, is probably the killer for this idea. Putting the parens around it doesn't change its type; any comma following will still be taken as an operator,() method call. So giving your objects operator,() methods will stop you being able to pass them as parameters to any function whose next argument matches one of the available method signatures.

    I am still absolutely repelled by the choice of << as the operator to overload for making builder patterns. I think it's horrid. At least % can be pronounced "modulo" which can kinda-sorta mean "in view of" or "taking into account" as well as "remainder when divided by".



  • @flabdablet said:

    So giving your objects operator,() methods will stop you being able to pass them as parameters to any function whose next argument matches one of the available method signatures.

    I actually just read the rule for this yesterdaywhy?. Commas used in an ambiguous context are always parsed as separators, not the comma operator.



  • @accalia said:

    and importantly the style check is completely automated.

    Do fucking code reviews. Now you're just throwing out a baby with the bathwater.

    In 99% of the cases, i++ is fine. If someone tries to pull stuff like i+++++i or something like that, they're in the minority, and it should be caught and dealt with on a code review.

    As in the Blakey's discussion - it's a people problem, not a technical one. Deal with people who do that.



  • That's what I thought too, hence my experiment upthread. My compiler settles for an addl $1,<register> instruction, plus the move from and to stack and register instructions.



  • @TwelveBaud said:

    Want me to actually write the code for that?

    I'd be interested to see the results of that, even if nobody else would...



  • @flabdablet said:

    That, actually, is probably the killer for this idea. Putting the parens around it doesn't change its type; any comma following will still be taken as an <code>operator,()</code> method call. So giving your objects <code>operator,()</code> methods will stop you being able to pass them as parameters to any function whose next argument matches one of the available method signatures.

    Unless you're really attached to the f("str"), ... (long tail of stuff) kind of stuff, you could tidy it up with a macro at this point, though. E.g.,
    #define PRINTF(x,...) (format(x),__VA_ARGS__) - __VA_ARGS__ seems to be supported by most compilers out there.

    C++11 largely fixes the problem with variadic templates, though.



  • @cvi said:

    C++11 largely fixes the problem with variadic templates, though.

    You also get the C99 preprocessor as part of C++11, which standardizes __VA_ARGS__, so you can write a variadic macro over your variadic template; may as well chuck in a lambda expression as well, why not, it's shiny...



  • Hey, say what you want about variadic templates, but they do allow you to write a type-safe printf() equivalent without resorting to overloading unrelated operators.



  • @cvi said:

    type-safe printf()

    If that meant that I would never have to deal with <iostream> again then I'm 100% in favour of it.



  • Yup, no more cout << stuff. I'm using tinyformat whenever possible. (It uses <iostream> internally, though.)


  • Discourse touched me in a no-no place

    @ben_lubar said:

    If I have -18446744073709551616 and I multiply it by -1, what do you get?

    Let's determine by experiment!

    % expr (-18446744073709551616)*(-1)
    18446744073709551616
    

    (Automatic use of bignums FTW! ;))


  • Discourse touched me in a no-no place

    @Maciejasjmj said:

    If someone tries to pull stuff like i+++++i or something like that, they're in the minority, and it should be caught and dealt with on a code review.

    I was thinking more about dealing with them in the car lot out back with a 2×4, but a code review works too.



  • @dkf said:

    I was thinking more about dealing with them in the car lot out back with a 2×4

    No, no, no, you do it on a code review, as a warning to the rest of your team.



  • @cvi said:

    tinyformat

    That seems pretty nice, actually!


Log in to reply