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