Re: Using templates to wrap OS APIs
"Scott Meyers" <smeyers@aristeia.com>
Somewhere, I figure this has got to be a FAQ. But I don't know where.
I want to wrap OS-dependent code in an OS-independent API to shield
clients
from having to know that, e.g., what's called Sleep under Windows is
called
usleep under Linux. No problem, I can use ifdefs:
#ifdef WINDOWS
inline void sleepMilliseconds( unsigned int uMilliseconds )
{ Sleep( uMilliseconds ); }
#else
inline void sleepMilliseconds( unsigned int uMilliseconds )
{ usleep( 1000 * uMilliseconds ); }
#endif
Good idea. Unless you really need inlines, you better go the tradiotional
path -- have a .h with interface, and a set of distinct .c?? files with the
implementation, that are different for each platform...
Except the preprocessor is gross. I'd rather use templates:
Why?
Isn't the point the abstraction only? The client only meets the interface,
and the implementation just needs to be correct... From that perspective
what could be more straightforward then the above code? What is the gain
from picking up more complexity?
struct Windows;
struct Linux;
template<typename OS> struct OSWrapper;
template<>
struct OSWrapper<Windows>
{
inline static void sleepMilliseconds( unsigned int uMilliseconds )
{ Sleep( uMilliseconds ); }
};
template<>
struct OSWrapper<Linux>
{
inline static void sleepMilliseconds( unsigned int uMilliseconds )
{ Sleep( uMilliseconds ); }
};
Unfortunately, this won't compile.
Suppose we had magic to make it did compile. How would I use it on the
client side?
Even with the appropriate
platform-specific #include directives, there is no Sleep on Linux and no
usleep on Windows, so one of the template specializations won't compile,
regardless of the platform on which I am compiling.
Yeah, see the consequence of struggling to create coupling instead of
preventing it. The 'normal' way would not impose inclusion of *anything*
from any system, just pure C/++ stuff, and the implementation files would
also know nothing about anything foreign...
While this kind of wrapping must include the whole world for everyone.
Templates without export support are suspect to these kind of problems -- so
not a good tool selection for the task.
The names Sleep and usleep are non-dependent names in the template, hence
looked up prior to instantiation.
Yeah, it worked "fine" back before the standard, then the model was reworked
to support exporting, and we have the combination of disadvantages ever
since. No luck here.
If I could somehow make them dependent
names, they'd be looked up only after instantiation, which would be fine.
The other traditional way is to use a hierarchy and virtual functions, that
is possible to use with templates too (though I honestly see just zero
elements here that would call or resemble the template way), with some luck
the compiler would use static calls in reality. but...
However, I can't make them dependent by prefixing the calls with "this->",
because the functions I'm in are static.
Is that so? I thought you can call a static function this way, was that a MS
extension in the early versions?
This compiles on Cameau:
struct Foo
{
static int Func() {return 1;}
};
int main()
{
Foo f;
return f.Func();
}
This too:
template <class T>
struct Bar
{
static int Func() {return 1;}
static int Func2() {return Func();}
int Func3() {return this->Func();}
};
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]