The Artima Developer Community
Sponsored Link

The C++ Source
What's Your Address
by Matthew Wilson
April 8, 2005

<<  Page 3 of 3

Advertisement

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].

In short, don't do it. In grepping through my source databases at the time of writing, I found eleven uses of it. Of the three that were used in "proper" classes—i.e. 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 sub-section.

26.4.2 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 non-standard 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 LPFILETIME ()
    {
      return reinterpret_cast<LPFILETIME>(p);
    }
    operator LPCFILETIME () const;
    operator ULARGE_INTEGER *()
    {
      return reinterpret_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;

Notes

  1. Of course, in an ideal world one would only have to read the documentation to understand, and memorably absorb, the fine nuances of the use of libraries such as those mentioned in this section. However, this is anything but the case. Documentation is at least one conceptual step away from the code-face, out-of-date the moment it's written, and difficult to write (either by the author of the code, who know's too much, or by another party, who know's too little). In reality, the code is often the documentation. [Glas2003].
  2. This was a definite case of not thinking before coding. The names BSTR and BStr are far too alike, and have caused me no end of bother.
  3. Keeping developers employed in remediation work doesn't count, since they'd be better off working on new projects, as would their employer
  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.

Resources

Matthew Wilson is author of Imperfect C++, which is available on Amazon.com at:
http://www.amazon.com/exec/obidos/ASIN/0321228774/

About the Author

Matthew Wilson is a software development consultant for Synesis Software, and creator of the STLSoft libraries. He is author of Imperfect C++ (Addison-Wesley, 2004), and is currently working on his next two books, one of which is not about C++. Matthew can be contacted via http://imperfectcplusplus.com/. Matthew is co-author with Bjorn Karlsson of The C++ Source column, Smart Pointers.

<<  Page 3 of 3


Sponsored Links



Google
  Web Artima.com   
Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us