Stuck on stupid

It happens to every programmer. It doesn’t happen often, but it does happen to everyone. You are looking at a piece of code or some debugging output, scratching your head and thinking ‘This is impossible’. It’s probably only 10 or 20 lines of code or text, and you’ve looked carefully at every line at least a dozen times over the last hour.


Every line makes sense and seems to mean exactly what you think it means, every word is in place, every semicolon is accounted for, and yet the sum of those lines is something completely unlike what you think it should be.


What makes matters even worse is that you just know that you are missing something obvious; something that anyone else would see after looking at the issue for less than 5 seconds. Maybe someone is even explaining the issue to you and you still don’t get it.


Well, I had such a moment just this weekend.


I was working on some C++ code that involved friend functions and streams. I’ve never done much with streams in C++, and never done anything with friends. Basically, friend declarations allow e.g. class A to specify that class B is allowed to access its private variables. Hence the joke: in C++, your friends can see your privates. I’ve never used it in a design, because if other classes need access to private data, the design is probably wrong.


There are some exceptions, where it can be extremely useful. One of these cases is if you have to create a serialization function that can serialize and deserialize an object. That function would need access to the private data to efficiently reconstruct the object, but you don’t want any other functions or classes to have the same privilege.


In my case, I had a template class that I wanted to reconstruct from a generic iostream. I implemented a global function that performed the reconstruction, but despite my friend declaration that gave istream access, the compiler didn’t agree. This is what my code boiled down to:


class A
{
friend istream;
int i;
};

istream& operator >>(istream& is, A &a)
{
is >> a.i; //error C2248: ‘A::i’ :
//cannot access private member declared in class ‘A’
return is;
}


 


I looked at it for a long time, read the ‘friend’ documentation in MSDN, consulted my copy of ‘The C++ programming language, 3ed’, asked a fellow MVP to explain it to me, and I still didn’t get it. I reasoned: ‘I just gave istream access to A::I, so WTF is the problem’.


In this case, the solution was really a ‘duh’ moment. Yes, istreams have access to A. But – and this is the whole issue – it is not the istream doing the accessing. The istream accesses an integer by reference; an integer that was retrieved by the global operator >> function. My code was functionally equivalent to:


istream& operator >>(istream& is, A &a)
{
int &temp = a.i; //error C2248
is >> temp;
return is;
}


And that is pretty obviously wrong. By writing is >> a.i, I lulled myself into believing it was ‘is’ who did the access, while in reality it was someone else.


As I said already: this was one of those ‘Aaargh’ moments where I just seemed to be stuck on ‘stupid’. Ah well. Live, learn, and have more coffee next time J Glad this doesn’t happen every day, week, or month.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>