Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 26.  What's Your Address?


26.4. What's Your Address: Coda

I hope I've managed to convince you that Peter was spot on. Overloading operator &() is just far too much trouble. Consider the amount of coding time, thinking time, and debugging time that is expended trying to understand and work with libraries that use it, I struggle to imagine how using it helps the software engineering community.[3]

[3] Keeping developers employed in remediation work doesn't count, since they'd be better off working on new projects, as would be their employers.

In short, don't do it. In grepping through all my source databases at the time of writing, I found eleven uses of it. Of the three that were used in "proper" classes—that is, those that are not in utility or meta-programming classes—I can probably truly justify only one of them. I removed two immediately.[4] The third I cannot justify, but I'm keeping it for reasons of expediency. For grins, I'll describe this in the following subsection.

[4] There's another reason to write a book: you get to go through all your own code and learn how much you didn't used to know.

26.4.1 A Sensationalist Backflip!

I'm not going to try to justify this to you; you can make up your own mind whether its utility outweighs the many good reasons against overloading operator &().

The Win32 API defines many nonstandard basic structures, oftentimes for closely related types. Further, since many Win32 compilers did not provide 64-bit integers in the early years of the operating system, there are several 64-bit structures that filled in the gap. Two such structures are ULARGE_INTEGER and FILETIME. Their structures are as follows:



struct FILETIME


{


  uint32_t    dwLowDateTime;


  uint32_t    dwHighDateTime;


};








union ULARGE_INTEGER


{


  struct


  {


    uint32_t  LowPart;


    uint32_t  HighPart;


  };


  uint64_t    QuadPart;


};



Performing arithmetic using the FILETIME structure is tiresome, to say the least. On little-endian systems, the layout is identical to that of ULARGE_INTEGER, so that one can cast instances of one type to the other; hence one can manipulate two subtract FILETIME structures by casting them to ULARGE_INTEGER and subtracting the QuadPart members.



FILETIME ft1 = . . .


FILETIME ft2 = . . .


FILETIME ft3;





GetFileTme(h1, NULL, NULL, &ft1);


GetFileTme(h2, NULL, NULL, &ft2);





// Subtract them – yuck!


reinterpret_cast<ULARGE_INTEGER&)(ft3).QuadPart =


  reinterpret_cast<ULARGE_INTEGER&)(ft1).QuadPart -


  reinterpret_cast<ULARGE_INTEGER&)(ft2).QuadPart;



This also is pretty tiresome, so I concocted the ULargeInteger class. It supplies various arithmetic operations (see Chapter 29), has a compatible layout with the two structures, and provides an operator &() overload. The operator returns an instance of Address_proxy, whose definition is shown in Listing 26.5.

Listing 26.5.


union ULargeInteger


{


private:


  struct Address_proxy


  {


    Address_proxy(void *p)


      : m_p(p)


    {}


    operator FILETIME *()


    {


      return static_cast<FILETIME*>(p);


    }


    operator FILETIME const *() const;


    operator ULARGE_INTEGER *()


    {


      return static_cast<ULARGE_INTEGER*>(p);


    }


    operator ULARGE_INTEGER const *() const;


  private:


    void  *m_p;


  // Not to be implemented


  private:


    Address_proxy &operator =(Address_proxy const&);


  };


  Address_proxy operator &()


  {


    return Address_proxy(this);


  }


  Address_proxy const operator &() const;


  . . .



It holds a reference to the ULargeInteger instance for which it acts, and it provides implicit conversions to both FILETIME* and ULARGE_INTEGER*. Since the proxy class is private and instances of it are only returned from the ULargeInteger's address-of operators, it is relatively proof from abuse, though you'd be stuck if you tried to put it in an STL container. But it considerably eases the burden of using these Win32 structures:



ULargeInteger ft1 = . . .


ULargeInteger ft2 = . . .





GetFileTme(h1, NULL, NULL, &ft1);


GetFileTme(h2, NULL, NULL, &ft2);





// Subtract them – nice syntax now


ULargeInteger ft3 = ft1 – ft2;




      Previous section   Next section