![]() | |
![]() ![]() |
![]() | 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: CodaI 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 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.
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; ![]() |
![]() | |
![]() ![]() |