|
|
|
Sponsored Link •
|
|
Advertisement
|
The Win32 API
Is like a sharp poke in the eye.
Its macros obtrusive,
Definitions abusive,
And coupling? A grown man could cry!
Sounds like the solution is going to be very important and you should pay close attention? Well, before we ascertain the truth of that, it'd probably serve us all to look at the problem itself.
The problem is quite simple: when selecting between alternate definitions of functions, or types, by discriminating the presence or value of preprocessor symbols, the derived symbols are defined as macros, which pervades the entire compilation unit subsequent to the point of definition. Yikes! That sounds horrid, to be sure, but what exactly does it mean? Naturally, we'll illustrate with a code example.
Consider the following header file, AcmeThreadingStuff.h:
/* AcmeThreadingStuff.h */ ACMELIB_EXTERNC void TheFuncST(void); /* This does single-threaded stuff */ #ifdef ACMELIB_MULTI_THREADING_SUPPORTED ACMELIB_EXTERNC void TheFuncMT(void); /* This does multi-threaded stuff */ #endif /* ACMELIB_MULTI_THREADING_SUPPORTED */ ...
This all looks okay so far. Ignoring the meaning of
ACMELIB_EXTERNC for the moment, it's
clear that there's a single-threaded version of
TheFunc, accessible in all builds,
and a multi-threaded version that's accessible when
AcmeLib determines that multi-threading constructs are
supported by the target environment. Let's look further
into the file:
/* AcmeThreadingStuff.h (continued) */ ... #ifdef ACMELIB_MULTI_THREADING # define TheFunc TheFuncMT #else /* ? ACMELIB_MULTI_THREADING */ # define TheFunc TheFuncST #endif /* ACMELIB_MULTI_THREADING */These five lines are provided as a convenience to the user, and select the appropriate version of TheFunc based on whether the current build settings are specifying a single-threaded, or a multi- threaded, compilation. (Note: For brevity we're assuming that the symbol
ACMELIB_MULTI_THREADING cannot be
defined in the absence of
ACMELIB_MULTI_THREADING_SUPPORTED.
Real-world header files would have a #error in there
somewhere to enforce this assumption.)
Now the user of this library can write code without having to worry overtly about the threading model:
#include "AcmeThreadingStuff.h"
int main()
{
TheFunc();
return 0;
}
(Note: This is the real world, so please don't assume from this simple example that writing correct multi-threaded code is just about calling the right function. That's anything but the case, but we're talking about macros in this installment, so let's leave it at that for now, wink, wink.)
You may have noticed that we have not yet said whether
the above code is compiled as C or as C++. As you're
likely well aware, operating system and third-party
libraries are generally packaged to provide C-APIs; for
an exhaustive (exhausting?) discussion as to why, consult
chapters 7 & 8 of Imperfect C++ [1]. One important reason
is that C is the lingua franca of inter-language
communication, since C modules can be directly linked to
other languages including C++, D, Delphi, Visual Basic
"Classic", assembler, Heron, and many others. C++ APIs
can only be interfaced to C++ client code, and there are
some serious restrictions even there [1]. Because C++ compilers
mangle the names of functions in order to facilitate
overloading, C functions visible in C++ compilation units
must have the linkage specification extern
"C" applied to them. Hence,
ACMELIB_EXTERNC is defined to be
extern "C" in C++ compilation, and as
extern (or as nothing at all) in C compilation units.
So, back to the code: what's the problem? Well,
TheFunc is a macro, which means it is
seen, and put into effect, at all points in the
compilation subsequent to its definition. Consider what
happens when the program is enhanced to use some code
from a C++ library from another vendor, BaRBSoft.
BaRBSoft defines the interface to its library in
BaRBSoftStuff.h, and provides the
implementation in a static library:
/* BaRBSoftStuff.h */
#include <string>
namespace BaRBSoft
{
int TheFunc(char const *regId, int *regIndex);
} // namespace BaRBSoft
We might change our main() function
as follows:
#include "BaRBSoftStuff.h"
#include "AcmeThreadingStuff.h"
int main()
{
TheFunc();
int regIndex;
BaRBSoft::TheFunc("Billy Kriesel", ®Index);
return 0;
}
Looks okay, does it not? Alas, this will not compile. The
compiler will tell you that the namespace
BaRBSoft does not contain a function
called TheFuncST (or
TheFuncMT, if you're building for
multithreaded, i.e. if the symbol
ACMELIB_MULTI_THREADING is defined).
What gives?
|
Sponsored Links
|