Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
sarehu
Apr 20, 2007

(call/cc call/cc)

Hubis posted:

Sorry, why is a pointer better than a non-const reference for things that can't be NULL?

I've never had any problems related to confusion over whether an output parameter can be NULL (generally, just say it can't be), and I've had very few non-serious problems, so this downside seems theoretical to me. Probably because they're typically assigned in the successful case, or they're some squirrelly thing you aren't taking for granted.

Likewise, the argument that output parameters should be generally avoided doesn't make any sense to me because when I'm programming it's often the best option, and I haven't faced harm from choosing that option. (And have faced clear benefit like not having to define new types or unpack a struct.)

Adbot
ADBOT LOVES YOU

Diametunim
Oct 26, 2010

Thanks for that! It totally gapped my mind that a linked list it supposed to maintain order when you add to the list. So this would have been a non-issue if the student had implemented his add-node function correctly.

ExcessBLarg!
Sep 1, 2001

Diametunim posted:

Thanks for that! It totally gapped my mind that a linked list it supposed to maintain order when you add to the list. So this would have been a non-issue if the student had implemented his add-node function correctly.
That's not a general property of a linked list though. In fact, most lists are unordered and insertions usually just go at the head because "it's easiest" and it's O(1).

You can, in any particular linked list implementation enforce an ordering constraint so that insertions are ordered. That makes each insertion event O(n) instead of O(1) though, although it does make it trivial to print out the sorted list.

Usually though an ordered binary tree is the preferred data structure to use when maintaining items in order.

raminasi
Jan 25, 2005

a last drink with no ice

Hubis posted:

Sorry, why is a pointer better than a non-combat reference for things that can't be NULL?

Because non-combat references have no place in the harsh world of C++ development :unsmigghh:

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

GrumpyDoctor posted:

Because non-combat references have no place in the harsh world of C++ development :unsmigghh:

I'm the close-quarters memory management.

Doctor w-rw-rw-
Jun 24, 2008

GrumpyDoctor posted:

Because non-combat references have no place in the harsh world of C++ development :unsmigghh:

It's a dangerous world out there. I need memory protection!

Hubis
May 18, 2003

Boy, I wish we had one of those doomsday machines...

GrumpyDoctor posted:

Because non-combat references have no place in the harsh world of C++ development :unsmigghh:

:stare:

I've been cross-posting in the D&D and C++ threads too much, apparently. Brains are funny things.

Hubis
May 18, 2003

Boy, I wish we had one of those doomsday machines...

sarehu posted:

I've never had any problems related to confusion over whether an output parameter can be NULL (generally, just say it can't be), and I've had very few non-serious problems, so this downside seems theoretical to me. Probably because they're typically assigned in the successful case, or they're some squirrelly thing you aren't taking for granted.

Likewise, the argument that output parameters should be generally avoided doesn't make any sense to me because when I'm programming it's often the best option, and I haven't faced harm from choosing that option. (And have faced clear benefit like not having to define new types or unpack a struct.)

It seems to me that confusing input and output variables is just as theoretical as 'null' pointers being passed. Your answer "just say it can't be" implies that someone's read the function spec, in which case any confusion over the role of parameters seems just as easily solved. And since there are plenty of reasons to pass pointers as inputs to functions, the argument that requiring output parameters to be pointers makes the code more readable doesn't really hold water for me either since it's going to be ambiguous at best.

Basically I can see the logic, but it doesn't seem either watertight or generalizable enough to me to be considered some kind of ironclad rule of Good Cee Plus Plus Programming Practices.

sarehu
Apr 20, 2007

(call/cc call/cc)
It's not logic, it's empirical experience. NULL pointers won't bite you.

(Your logic is bad though, because if somebody hasn't read the function spec, then they can't assume it's OK to pass a null pointer.)


Also, function input parameters will generally be by const reference, so it's going to be clear at the callsite whether something's modified by the function or not, with the false positives in the appropriate direction (that something could be written when really it's a pointer-to-const for some reason).

Edit: What I mean is, other stuff that gets passed by pointer won't be output parameters, but they'll still be modified. The whole purpose of passing by pointer is to make the code be non-deceptive at the callsite. Somebody looking at the function signature can easily see whether it's a non-const or const reference or what, after all.

sarehu fucked around with this message at 18:48 on Nov 23, 2015

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today
Another way to make it clear that a variable is an output is good name choice. Of course, this all becomes a non-issue if you just output data by returning it instead.

Xerophyte
Mar 17, 2008

This space intentionally left blank
Matrix4f::inverse(object_to_world, &world_to_object) is definitely clearer on what transform is being computed from what over Matrix4f::inverse(object_to_world, world_to_object), if you're consistent about pass-by-pointer implying output variable. Hardly worth any holy wars; it's not brace style.

sarehu
Apr 20, 2007

(call/cc call/cc)
Matrix4f::inverse(object_to_world,  world_to_object )

Just put extra whitespace around output parameters, that should clear things up.

Xarn
Jun 26, 2015
code:
world_to_object = Matrix4f::inverse(object_to_world)
Seems clear enough.

Xerophyte
Mar 17, 2008

This space intentionally left blank

Xarn posted:

code:
world_to_object = Matrix4f::inverse(object_to_world)
Seems clear enough.

Feel free to imagine that I used an example bigger than 64 bytes if that's below your pass-by-value threshold. Point is:
1) Sometimes you want functions with output variables.
2) These could be pointers or non-const references.
3) There are readability advantages to consistently making outputs pointers and inputs const references.

VikingofRock
Aug 24, 2008




What about cases where something is not really logically an output parameter, but it does get modified along the way? For example, something like this:

C++ code:
boost::optional<T> single_pass_parse(const parser<T>& parser, std::string& remaining_input); 
where remaining_input is modified to be the input remaining after the parser either succeeds or fails.

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today
I would instead return tuple<optional<T>, std::string::iterator> or something analogous. Avoids copying/moving your entire remaining input (which might be arbitrarily large) as a bonus.

VikingofRock
Aug 24, 2008




Ralith posted:

I would instead return tuple<optional<T>, std::string::iterator> or something analogous. Avoids copying/moving your entire remaining input (which might be arbitrarily large) as a bonus.

Oops--you're right, I probably should have made it take an std::string::iterator& instead of an std::string&. I still think that use of a reference it totally fine though.

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

VikingofRock posted:

What about cases where something is not really logically an output parameter, but it does get modified along the way?
Shoot the programmer and switch to string_view rather than using mutable in/out parameters.

sarehu
Apr 20, 2007

(call/cc call/cc)

VikingofRock posted:

What about cases where something is not really logically an output parameter, but it does get modified along the way?

I say, use a pointer. Simple as that.

Using a string_view still means you'll want to mutate the string_view.

Bureaucratically returning a std::pair and having to juggle .first/.second stuff is worse.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
In my experience, a function with a single out/inout parameter is not a very stable situation, and it's better to bite the bullet and make it a method on a helper type. But then, it tends to be recursion that forces my hand, and I am probably at an extremum in terms of how much recursive code I write in imperative languages.

Anyway, I agree with shrughes on the larger point. Tuple and optional results are terrible in C/C++; you just get forced into so many bad code patterns in order to make them work without proper language support for decomposition and matching. (And it hurts me to say this, because there are half-a-dozen minor abstraction costs associated with returning via a formal out-parameter.) Out/inout parameters are comparatively tame, with just the one major problem: by-reference arguments aren't obvious at the call site. That is largely fixable by just passing the argument by pointer instead, and you can pretty easily avoid people passing null by just asserting that they didn't. Plus, passing by pointer lets you avoid ancient and unfixable MSVC badness if you happen to be on that platform, so what's not to like?

raminasi
Jan 25, 2005

a last drink with no ice
Clearly, you should just use std::tuple and std::tie :suicide:

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed
Or travel a year or two into the future and use P0151.

icantfindaname
Jul 1, 2008


edit: Okay, I fixed the problem. But I'm still interested in what the problem actually was. The string behavior is loving bizarre: When I try to append one string to another it's just replacing the first X characters of the first string with the second. Removing the final character of the first string fixes it but what is that character doing even?

code:
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include "BinaryStringTree.h"

int main ()
{
        BinaryStringTree *tree = new BinaryStringTree();

        std::ifstream input;
        std::string line, first, last;
        input.open ("hash_short");

        if (input.is_open ()) {
                while (std::getline (input, line)) {
//                      std::cout << line << std::endl;

                        int i = 0;
                        while (i < line.size()) {
                                if (line[i] == '#') break;
                                i++;
                        }

                        last = line.substr (i + 1, std::string::npos);
                        first = line.substr (0, i + 1);


                        std::cout << first << std::endl << last << std::endl;

                        last.append (first);

                        std::cout << last << std::endl;

                        tree->insertNode (last);
                }

                input.close ();
        }



//      std::cout << "\nIn-order traversal of this tree: \n";
//      std::cout << std::endl;
//      tree->getInOrderTraversal (tree);
//      std::cout << std::endl;

        return 0;
}
Output:

code:
1, #
yapbozmeb
1, #ozmeb
2, #
gameinsight
2, #insight
3, #
android
3, #oid
4, #
rt
4, #
5, #
premiosgoya
5, #iosgoya
6, #
androidgames
6, #oidgames
7, #
goya2014
7, #2014
8, #
ipad
8, #
9, #
ipadgames
9, #games
10, #
birnesilgeliyor
10, #silgeliyor

icantfindaname fucked around with this message at 08:09 on Nov 26, 2015

stochastastic
Jun 5, 2003

icantfindaname posted:

edit: Okay, I fixed the problem. But I'm still interested in what the problem actually was. The string behavior is loving bizarre: When I try to append one string to another it's just replacing the first X characters of the first string with the second. Removing the final character of the first string fixes it but what is that character doing even?

Check your input file for control characters. For instance, repeated backspace control characters would cause something like this, e.g.
1, #yapbozmeb^H^H^H^H^H^H^H^H^H

Also, why not use find? line.find('#') -- also check the return value. There's a bug in this program if there is nothing after a '#'.

stochastastic fucked around with this message at 04:03 on Nov 28, 2015

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today
Try compiling with AddressSanitizer or running inside valgrind.

Zopotantor
Feb 24, 2013

...und ist er drin dann lassen wir ihn niemals wieder raus...

stochastastic posted:

Check your input file for control characters. For instance, repeated backspace control characters would cause something like this, e.g.
1, #yapbozmeb^H^H^H^H^H^H^H^H^H

More likely a CR (\r) if removing a single character fixes it. That might happen when reading in binary mode on Windows.

vileness fats
Sep 16, 2003

College Slice

Xerophyte posted:

Feel free to imagine that I used an example bigger than 64 bytes if that's below your pass-by-value threshold. Point is:
1) Sometimes you want functions with output variables.
2) These could be pointers or non-const references.
3) There are readability advantages to consistently making outputs pointers and inputs const references.

You guys are so excited about coding standards :lol:

I couldn't resist this one though - there are obviously _no_ readability advantages to making outputs pointers, as there is a very good chance the variable you are passing in is _already_ a pointer... or are you all hungarian notation users?

Also, how do you distinguish between optional outputs, and mandatory outputs, and deal with the maintenance case where that may change if the arg is just a pointer?

Output params as pointers is a C-ism. It's not a big deal, and output params are not common in any case, but you're fooling yourself if you think future maintainers are going to thank you for it.

It can also be a fine line between an output param, and just an argument that gets modified by a function.

Is this thread on board with do_something(foo) is better than foo.do_something() if do_something can be efficiently expressed in terms of foo's public interface'(see: holy poo poo 15 years ago?). If so how do you reconcile that with ''non-combat references bad!"

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

vileness fats posted:

You guys are so excited about coding standards :lol:

I couldn't resist this one though - there are obviously _no_ readability advantages to making outputs pointers, as there is a very good chance the variable you are passing in is _already_ a pointer...

If you use pointers instead of references all over the place in the rest of your C++ code, then I'm not surprised that you're not a fan of coding standards.

vileness fats
Sep 16, 2003

College Slice

Jabor posted:

If you use pointers instead of references all over the place in the rest of your C++ code, then I'm not surprised that you're not a fan of coding standards.

In case of reading comprehension failure, I'm advocating references over pointers here.

Everyone else seems to be advocating pointers over references for output parameters - as a coding standard no less.

Edit: Well, strictly speaking I'm not advocating anything, just pointing out that the main argument for using pointers in this situation is bogus. However, I'm sure my bias is showing...

vileness fats fucked around with this message at 07:08 on Nov 29, 2015

vileness fats
Sep 16, 2003

College Slice
The argument for pointers over references for output args for readability is essentially self-refuting in any case as it is not transitive (example illustrative only):

void do_something_else(foo* foo);

void do_something(foo* foo)
{
//...
do_something_else(foo);// Obviously an output param at the call-site?
}

my bike shed is green itt

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Your statement about forwarded out-parameters is true but misses the point.

First, the readability argument is that it's sometimes important to be able to easily understand what places modify storage. That goal isn't undermined by implicit forwarding for a simple reason: this kind of "where was this modified" investigation pretty much always starts with understanding where the storage comes from in the first place. In other words, if you're reading a function that has an out-parameter, and you're looking at a call that forwards that out-parameter down to another function, you're almost certainly already aware that (1) it's an out-parameter and (2) a pointer, and therefore that passing it down will still allow mutation of the original storage. So there's not much real potential for surprise or confusion here.

Second, in any given code base, you probably have a lot more call sites that pass variables as out-parameters than function implementations that receive them, and it is reasonable to dictate a code style that tries to solve problems for the former, especially if it's not particularly harmful for the latter.

It would certainly be better to have markers on all call sites, and that's why Swift's inout is designed the way it is. But in the absence of a better language design, it is still an improvement to limit the readability damage of out-parameters.

vileness fats
Sep 16, 2003

College Slice
The forwarded argument example is just me being pedantic, not the real meat of my argument.

Any comment on the other points?

I still think you're wrong in practice regarding readability (at least in my experience), but each to their own.

I can justify further, but in the end it's a judgement call so I doubt you want to hear it.

xgalaxy
Jan 27, 2004
i write code
I prefer C#'s ref to Swift's inout. Its the same syntax at both the definition and call site.

vileness fats
Sep 16, 2003

College Slice
To clarify on the readability point:

rjmccall posted:

Second, in any given code base, you probably have a lot more call sites that pass variables as out-parameters than function implementations that receive them, and it is reasonable to dictate a code style that tries to solve problems for the former, especially if it's not particularly harmful for the latter.

That right there is the judgement call. It may try to solve the problem, but it fails if the parameter is a pointer (smart, raw, or forwarded) - so it cannot be relied on. It does cause problems as null parameters have to handled correctly... there is a reason references are generally preferred to pointers after all.

So in my view, does it help? Not in a reliable way. Does it hurt? Yes, potentially in terms of both correctness & efficiency. Therefore, bad rule.

If we'd like to appeal to authority:

Google's style guide mandates the use of pointers for out-params.... but google is the acknowledged home of stupid coding standards.
C++ Coding Standards recommends references unless the out-param is optional.

No consensus in industry about best practice.... so not a good candidate for a coding style rule, especially give it's fairly rare, and not something anyone is going to get confused about anyway.

It's a good interview question, thinking about it. As the interviewer you can see if you can have a style discussion with the candidate on a topic that has no clear correct answer without anyone getting too excited.
As the interviewee if the group you're considering has mandated rules for arbitrary trivial details like this, well, that's a big red flag.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

vileness fats posted:

As the interviewee if the group you're considering has mandated rules for arbitrary trivial details like this, well, that's a big red flag.

Arbitrary trivia that doesn't matter too much either way is exactly the scenario in which you want style rules. Since it's basically arbitrary, the benefits of being consistent with the rest of the code far outweight any other consideration.

See also: where you choose to put the braces.

FamDav
Mar 29, 2008
When are we going to get the out<T> container to solve this horrible mess???

xgalaxy
Jan 27, 2004
i write code

FamDav posted:

When are we going to get the out<T> container to solve this horrible mess???

I was going to say can't you just create a template wrapper class to use on parameters that are in/out parameters and have it compile down to no-ops essentially. I guess any function that needs a parameter like this is a little messier to call.. but that is kind of the point.

vileness fats
Sep 16, 2003

College Slice

Jabor posted:

Arbitrary trivia that doesn't matter too much either way is exactly the scenario in which you want style rules. Since it's basically arbitrary, the benefits of being consistent with the rest of the code far outweight any other consideration.

See also: where you choose to put the braces.

Perhaps, I tend to disagree but there you go.

Whitespace & formatting rules serve a practical purpose - stopping endless reformatting commits wrecking your diffs & generating pointless hard to read patches... because no-one is 100% disciplined about separating formatting commits from code changes.

However, maybe the end is in sight? With Clang code format actually working off a compiled AST (not just text) - it's very, very good and never breaks code. If you haven't used it, check it out (also include-what-you-use, etc). In principle each of my devs could write their own formatting rules, automatically have them applied when code is checked out, but have the "standard" rules applied as commit rule. So then everyone could happily code away using their own preferred formatting without it breaking diffs, or stuffing up patches. Crazy times.

In practice though everyone uses Allman so it's not an issue ;)

Plorkyeran
Mar 22, 2007

To Escape The Shackles Of The Old Forums, We Must Reject The Tribal Negativity He Endorsed

xgalaxy posted:

I was going to say can't you just create a template wrapper class to use on parameters that are in/out parameters and have it compile down to no-ops essentially. I guess any function that needs a parameter like this is a little messier to call.. but that is kind of the point.

C++ code:
template<typename T>
class out : public std::reference_wrapper<T> {
    explicit out(T& value) noexcept : std::reference_wrapper<T>(value) { }
    out(const out&) noexcept = default;
};

template<typename T>
auto out_ref(T& v) { return out<T>(v); }

void foo(out<int> x) {
    x.get() = 10;
}

int y;
foo(out_ref(y));
Should compile to the same thing as a pointer out param.

Adbot
ADBOT LOVES YOU

Hubis
May 18, 2003

Boy, I wish we had one of those doomsday machines...

Plorkyeran posted:

C++ code:
template<typename T>
class out : public std::reference_wrapper<T> {
    explicit out(T& value) noexcept : std::reference_wrapper<T>(value) { }
    out(const out&) noexcept = default;
};

template<typename T>
auto out_ref(T& v) { return out<T>(v); }

void foo(out<int> x) {
    x.get() = 10;
}

int y;
foo(out_ref(y));
Should compile to the same thing as a pointer out param.

This. I like this.

Of course, then you have to decide what's an output parameter, and what's a pointer you pass to a function that just happens to have side effects (and how you can guarantee it). Ultimately, "output parameters" are just a hazy semantic concept in C++, not an actual code construct.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply