Welcome to MSDN Blogs Sign in | Join | Help

Creating write-only code: the Reference (&) Operator

Write-only code is a term used to describe code that can easily be written, but that no one but the original author can easily read. Since invariably far more time is spent reading code than writing it, this is NOT a positive term.

Recently, while participating in a code review, I was reminded of one of my least-favorite C++ features -- the ability to use a reference declarator in a function. Most code that needs to change a variable of the caller is written like this:

void foo(long *a) { *a = 3; } void caller() { long var = 0; foo(&var); }

However, by using the syntax "type-id & cast-expression" in the function declaration, you can write this:

void bar(long &a) { a = 3; } void hard_to_read_caller() { long var = 0; bar(var); }

If you look at the assembly code generated, it's exactly the same in both cases. The (x86, unoptimized) calling code looks like this:

lea eax,[ebp-8] push eax call @ILT+459(?foo@@YGXPAJ@Z)

So what's the difference? In the first example, it's obvious from the calling code that the variable 'a' can get modified. You don't know for sure that it will, but since you're obviously passing the address of the variable, you know it's a possibility. Now you can go check the definition of 'foo' to see how it might change your variable.

In the second example, there is no clue at all from the calling code. The call to bar looks no different than if bar were defined as 'void bar(long a)'. Without looking up the function prototype for bar, you'd assume that var won't be modified. In short, this is write-only code.

This is one of the (many) things that the C# team got right. You must use the ref or out keywords from the calling code when passing variables to a function that can change them (ref for input/output variables, out for output-only variables).

Published Monday, September 18, 2006 9:06 PM by mikedodd

Comments

# re: Creating write-only code: the Reference (&) Operator

" ... Without looking up the function prototype for bar, you'd assume that var won't be modified. ... "

No, i wouldn't assume that, because my first programming language was Pascal, where i always had to check the function declaration to see if a parameter is passed by value or reference (VAR).

Most likely you assume the parameter is passed by value, because you started with C, where all parameters are passed by value. Even reference parameters (&foo) use pass-by-value, cause the caller passes the address of the parameter to the callee.

With C++ however, the rules have changed. But you dont need C++ to run into problems with your assumption. In pure C, there might be a macro like

 #define foo(x) TheRealFoo(&x)

which hides the fact that TheRealFoo modifies its parameter. So your assumption is wrong even in pure C.

I would agree, that a macro definition like that is not very wise.

In my humble opinion, reference parameters in C++ are better than C's pass-by-reference emulation using pass-address-by-value, because in your examples you can call foo(null), but you cant call bar(null).

I would agree, that it would be nice to be able to understand a call without checking the declaration of the callee.

What do we learn from that ?

- We should never assume anything unless the language explicitely guarantees it.
- The origin of most errros is the subtle difference between what we meant and what we actually wrote ..
- We should learn many different languages so that we understand our preferred language (and its limits and quirks) better. The true Zen masters think at a higher abstraction level that is not tied to the limits of just one language.

Speaking of Zen masters ...

Anders Hejlsberg and the rest of the C# team realized the problem that programmers with different backgrounds read different things into the same code and therefore removed the ambiguity by introducing the required keywords ref/out. C# is full of wise definitions that makes it real hard to shoot ourselfes, e.g. try to loop n-1 or n+1 times over n things with foreach ...
Tuesday, September 19, 2006 2:49 AM by MillKa

# re: Creating write-only code: the Reference (&) Operator

This could open a can of worms...

I'll just chip in with my belief that in general it is better to pass by reference than by pointer. The key thing to remeber is that the called function has to be considerably less 'defensive' and the call itself is less error prone. Why? Simple, a reference has to reference something, while a pointer doesn't. And unless the pointer has been setto NULL it's pretty tricky to know if you're looking at an unitialised pointer. On the other hand if you write a function to consume a reference to a type, you at least know you'll always get something to work with! Well, not always but you have to go along way out opf your way to break it ;-)

I agree that by simply looking at the function call it's hard to see what's going on, but these days Intellisense or any other good development environment will ease that pain. For me the trade of betwean safe code or marginally less code is a no-brainer.
Tuesday, September 19, 2006 3:44 AM by John C

# re: Creating write-only code: the Reference (&) Operator

The Reference Operator in C++ is very useful if you don’t want a copy of your data type. To prevent the function from writng you can use the const keyword. The same is true in C#. If you use reference types the function can change your variable.
Tuesday, September 19, 2006 3:51 AM by Marc

# re: Creating write-only code: the Reference (&) Operator

Hmph. I'm sure there are plenty of other ways in C# to write write-only code. e.g. inappropriate overloading of other operators.

Yes, you shouldn't use any of them. Yes, decent software projects will have coding guidelines outlawing them. But then, most people don't use non-const reference parameters in C++, and most decent software projects will have guidelines not to use them.

And the C# solution isn't "all that" either. Although callees cannot change the object the caller's variable points to, they can still modify objects by calling methods that alter them (mutators). Without an equivalent of C++'s logical constness, there's no way of knowing whether or not the object you pass to a function will in any way resemble itself by the time the function returns.
Tuesday, September 19, 2006 4:36 AM by Adam

# re: Creating write-only code: the Reference (&) Operator

I'm not sure I get how the const operator of C++ guarantees anything, either. If you pass a const pointer to a function, that function can still change the data that's being pointed to.

To the Intellisense comment: yes, this can help a lot. But Intellisense or any other development environment isn't a good substitute, I think, for being able to just read through the code.
Wednesday, September 20, 2006 12:59 AM by mikedodd

# re: Creating write-only code: the Reference (&) Operator

Re the const operator and pointers

Remeber

(const int* pI) != (const int* const pI)

And if you don't know the difference I sugest revisiting a good C++ book!

In truth I've met very few people who remember that to make a pointer and it's contents truelly const you have to use the second syntax, but it's part of the language...
Wednesday, September 20, 2006 3:30 AM by JohnC

# re: Creating write-only code: the Reference (&) Operator

Mike, not a const pointer to an object, a pointer to a const object. The difference between the following:

void foo(long * const a)
{
   *a = 3; /* OK */
}

void foo(long const * a) /* same as (const long * a) */
{
   *a = 3; /* Compile error */
}

Is that in the first "a" is a const-pointer-to a long and will work, while in the second "a" is a pointer-to a const-long and will give a compile time error. (Hint: Read the declaration backwards)

The advantage of this is that you can do:

void bar(some_big_object const & a)
{
   a.get_property(); /* OK */
   a.set_property(3); /* Compile error */
}

so that you pass a reference (pointer) to a large object that you don't want copied, but still guarantee to the caller that you're not going to modify the item.

The compiler knows which methods can be called on a const object by whether or not they've been declared with the "const" modifier. e.g.

class some_big_object
{
   // ...
   int get_property() const;
   void set_property();
   // ....
};

There's /no/ way to do this in C#


I'll disagree with John C though. If the callee modifies the item (i.e. it is effectively a "ref" or "out" parameter) then I'd say you should pass by pointer. That way, you can tell from the call site whether or not the parameter you're passing in might be modified.
Wednesday, September 20, 2006 6:21 AM by Adam

# re: Creating write-only code: the Reference (&) Operator

In fact, the best explanation is probably on the C++ faq lite -

http://www.parashift.com/c++-faq-lite/const-correctness.html
Wednesday, September 20, 2006 7:04 AM by Adam

# re: Creating write-only code: the Reference (&) Operator

Just use Objective-C. It's much better :-) This all gets to a more general point. You should be able to understand what source code is doing without having to refer to the .h file. When someone says "Didn't you check the .h file?" I want to strangle them. I don't have to refer to a dictionary or encyclopedia to understand a well written book. I should similarly be able to understand source code without having to check other references. It should be obvious from the calling code what types the parameters are and whether they might be altered. JR
Wednesday, October 04, 2006 12:51 AM by JR

# Mike Dodd s blog Creating write only code the Reference amp Operator | bar stools

New Comments to this post are disabled
 
Page view tracker