Choose between class implementations at compiletime

  • Thread starter Christoph Mathys
  • Start date
C

Christoph Mathys

Hello!

Well, nothing too spectacular, but I'd still be interested in how one
solves this problem in a clean way: I need to a implement a class
which uses different APIs and provides a uniform interface (APIs are
fixed at compiletime). I can't use an ABC directly because the class
is only used as value type. But I'd still like to have an interface
common to all classes, defined at a single place.

I had the following ideas to this problem:
1. Implement a completely independent version of the class for every
platform. This is easy to do and to use, but you don't have any kind
of common interface between the classes except convention alone.

2. Use a template class which just declares the method (some kind of
static interface) and fully specialize the class for every platform.
However, after a bit more thinking this solution does not seem to have
much advantages as the compiler will only give an error if you
actually try to invoke a missing function (e.g. not specialized, only
declared). You have however a common place were methods are declared
which *should* be there.

3. Define an ABC and pimpl it inside an implementation providing the
needed NVI. I don't need to squeeze cycles, but seems a bit overkill
for something that is known at compiletime.

Some toy code for variant 2 is below.
Thanks for ideas!
Christoph



#include <string>

template<typename T>
struct MyInterface {
std::string get() const;
void blup();
T node_;
};

struct AsdfApi {
std::string get_name() const
{ return name_; }
std::string name_;
};

struct XyzApi {
std::string GetName() const
{ return name_; }
std::string name_;
};

#ifdef USE_ASDF
template<> std::string MyInterface<AsdfApi>::get() const
{ return node_.get_name(); }
typedef MyInterface<AsdfApi> IfToUse;
#else
template<> std::string MyInterface<XyzApi>::get() const
{ return node_.GetName(); }
typedef MyInterface<XyzApi> IfToUse;
#endif

int main()
{
IfToUse use;
use.get();
// use.blup(); // compile error if uncommented
return 0;
}
 
A

AnonMail2005

Well, nothing too spectacular, but I'd still be interested in how one
solves this problem in a clean way: I need to a implement a class
which uses different APIs and provides a uniform interface (APIs are
fixed at compiletime). I can't use an ABC directly because the class
is only used as value type. But I'd still like to have an interface
common to all classes, defined at a single place.

I had the following ideas to this problem:
1. Implement a completely independent version of the class for every
platform. This is easy to do and to use, but you don't have any kind
of common interface between the classes except convention alone.

2. Use a template class which just declares the method (some kind of
static interface) and fully specialize the class for every platform.
However, after a bit more thinking this solution does not seem to have
much advantages as the compiler will only give an error if you
actually try to invoke a missing function (e.g. not specialized, only
declared). You have however a common place were methods are declared
which *should* be there.

3. Define an ABC and pimpl it inside an implementation providing the
needed NVI. I don't need to squeeze cycles, but seems a bit overkill
for something that is known at compiletime.
Can you define the concrete class (e.g. Foo) in a header file but
implement N implementations of Foo in various .cpp files that
correspond to your APIs and put each in a separate library?

Then it becomes a linking issue - which implementation to link with.
Of course this would only work if you require one implementation per
program.

HTH
 
C

Christoph Mathys

Can you define the concrete class (e.g. Foo) in a header file but
implement N  implementations of Foo in various .cpp files that
correspond to your APIs and put each in a separate library?

Yes, of course! Why didn't I think of that... Thanks for the hint!

Christoph
 
A

AnonMail2005

Yes, of course! Why didn't I think of that... Thanks for the hint!

Christoph

But I must say, that can complicate your build and also your testing.

Using and Abstract Base Class and some sort of a factory that returns
the correct derived class at run time based on a configuaration
parameter might be easier overall in the long run.

HTH
 
A

Alf P. Steinbach

* (e-mail address removed):
But I must say, that can complicate your build and also your testing.

Using and Abstract Base Class and some sort of a factory that returns
the correct derived class at run time based on a configuaration
parameter might be easier overall in the long run.

That's a Java technique.

In standard C++ there are no shared libraries, hence with that solution as an
*alternative* to the linking, all the code for the different systems would have
to be present, which is just not practical.

With a shared library the Java factory technique becomes practically possible,
but then it's most likely, depending on the app, just a redundant layer of
complication, since the basic shared library dynamic linking does it all.

In short, transferring Java patterns uncritically to C++ isn't a good idea.

They're two different languages, and in particular in this case, with ordinary
C++ you build a different executable for each platform (link time selection is a
good idea), while with Java you may build a common executable for all platforms
(run time selection, implying a factory, is a good idea).


Cheers & hth.,

- Alf

PS: Assuming I got your identity right, I think that may be one thing your
programmers reacted to, namely needless and quite unnatural Java-ism. ;-)
 
A

AnonMail2005

* (e-mail address removed):




That's a Java technique.

In standard C++ there are no shared libraries, hence with that solution as an
*alternative* to the linking, all the code for the different systems would have
to be present, which is just not practical.

With a shared library the Java factory technique becomes practically possible,
but then it's most likely, depending on the app, just a redundant layer of
complication, since the basic shared library dynamic linking does it all.

In short, transferring Java patterns uncritically to C++ isn't a good idea.

They're two different languages, and in particular in this case, with ordinary
C++ you build a different executable for each platform (link time selection is a
good idea), while with Java you may build a common executable for all platforms
(run time selection, implying a factory, is a good idea).

Cheers & hth.,

- Alf

PS: Assuming I got your identity right, I think that may be one thing your
programmers reacted to, namely needless and quite unnatural Java-ism. ;-)

He didn't specify how different the classes would be so I assumed
putting all the code into a library and using a factory was
practical. If each class is really an entire subsytem than it's not
practical.

I never mentioned anything about Java. ABC and factory are common
design patterns that are used in C++ and many other languages.

Not sure what you are referring to w.r.t my identity.
 
J

Joshua Maurice

Hello!

Well, nothing too spectacular, but I'd still be interested in how one
solves this problem in a clean way: I need to a implement a class
which uses different APIs and provides a uniform interface (APIs are
fixed at compiletime). I can't use an ABC directly because the class
is only used as value type. But I'd still like to have an interface
common to all classes, defined at a single place.

I had the following ideas to this problem:
1. Implement a completely independent version of the class for every
platform. This is easy to do and to use, but you don't have any kind
of common interface between the classes except convention alone.

Could you not do something like:

class your_class
{
public:
void some_func();
void some_func_2();
//...
private:
#if defined WIN32
HANDLE h;
#elif defined POSIX_SYSTEM
//...
#endif
};

#if defined WIN32
void your_class::some_func()
{ //windows impl
}
//...
#elif defined POSIX_SYSTEM
void your_class::some_func()
{ //POSIX impl
}
//...
//...
#endif

It's not the prettiest, but at least there's more than convention
holding your class together. Instead of ifdefs, you could put them in
separate cpp files and have the platform build rules select which impl
to compile into your lib (similar to an approach already mentioned in-
thread).
 
C

Chris M. Thomasson

Christoph Mathys said:
Hello!

Well, nothing too spectacular, but I'd still be interested in how one
solves this problem in a clean way: I need to a implement a class
which uses different APIs and provides a uniform interface (APIs are
fixed at compiletime). I can't use an ABC directly because the class
is only used as value type. But I'd still like to have an interface
common to all classes, defined at a single place.

[...]


Perhaps you could try something like:

<pseudo-code typed in newsreader>
________________________________________________________________
// your_lib_windows.hpp
#include <windows.h>


namespace your_lib_windows
{
class mutex
{
CRITICAL_SECTION m_mutex;


public:
void lock() throw() {...}

void unlock() throw() {...}
};
}

namespace your_lib_os = your_lib_windows;






// your_lib_posix.hpp
#include <pthread.h>


namespace your_lib_posix
{
class mutex
{
pthread_mutex_t m_mutex;


public:
void lock() throw() {...}

void unlock() throw() {...}
};
}

namespace your_lib_os = your_lib_posix;






// your_lib.hpp
#if defined (WINDOWS_PLATFORM)
# include "your_lib_windows.hpp"
#elif defined (POSIX_PLATFORM)
# include "your_lib_posix.hpp"
#else
# error Platform Not Supported!
#endif


namespace your_lib
{
namespace os = your_lib_os;
}






// main.cpp
#include "your_lib.hpp"


int main()
{
{
your_lib::eek:s::mutex mtx;

mtx.lock();

mtx.unlock();
}

return 0;
}

________________________________________________________________


;^)
 
T

Tech07

I never mentioned anything about Java. ABC and factory are common
design patterns that are used in C++ and many other languages.

Indeed. Programming with interfaces existed way before Java did. How far
back the origins of the technique go, I don't know. Probably from the '60's,
where all other concepts being used in programming languages today came from
;).
 
A

Alf P. Steinbach

* Tech07:
Indeed. Programming with interfaces existed way before Java did. How far
back the origins of the technique go, I don't know. Probably from the '60's,
where all other concepts being used in programming languages today came from
;).

The context here is, quote, to support "every platform".

Using a factory to provide platform independence is a Java technique.

While AnonMail2005 didn't mention the word "Java", after first providing
sensible advice he then recommended an unsuitable-for-C++ Java technique.

It is unsuitable because for C++ you do not, in general, provide the same
executable for different platforms, as can be the case with Java.

With C++ you provide different executables for different platforms. Get it?


Cheers & hth.,

- Alf
 
T

Tech07

Alf said:
* Tech07:

The context here is, quote, to support "every platform".

Maybe... I jumped in on an "Re:", but in response to:

"Using and Abstract Base Class and some sort of a factory that returns
the correct derived class at run time based on a configuaration
parameter might be easier overall in the long run."

you said:

"That's a Java technique"

So maybe you should try somehow to convey in your posts esoteric things that
are likely to be taken as "rule of thumb", should that be the scenario.
 
A

Alf P. Steinbach

* Tech07:
Maybe... I jumped in on an "Re:", but in response to:

"Using and Abstract Base Class and some sort of a factory that returns
the correct derived class at run time based on a configuaration
parameter might be easier overall in the long run."

you said:

"That's a Java technique"

That was an utterance in context, as is everything here.

When you "jump in", read the thread first.

And before you "jump in", try to get some familiarity with Usenet in general,
say for some weeks (not just this group, although that's recommended: see the FAQ).

So maybe you should try somehow to convey in your posts esoteric things that
are likely to be taken as "rule of thumb", should that be the scenario.

You're saying, *because* of your limited ability to read and understand
(demonstrated above), I should try to convey "esoteric things"?

Don't you think you'd have even more trouble with esoteric things than with
fundamental, basic, absolute beginner's things like the above?



Cheers & hth.,

- Alf
 
J

James Kanze

* (e-mail address removed):

I'd be interested in hearing how. It's the standard technique,
and I've not found it to cause any particular problems for the
build or for testing.
That's a Java technique.

It's also a C++ technique. I think what you meant to say is
that it's the only technique available in Java, so people used
to Java end up using in even in cases like this where it is
totally inappropriate. (When all you have is a hammer,
everything looks like a nail.)
In standard C++ there are no shared libraries,

In C++ as it is actually practiced, there are, and it's actually
common practice to use something like this, e.g. loading a
different look and feel for a GUI according to an environment
variable.
hence with that solution as an *alternative* to the linking,
all the code for the different systems would have to be
present, which is just not practical.

Depending on what the different versions do, the code for one
platform might not even compile on another.`
With a shared library the Java factory technique becomes
practically possible, but then it's most likely, depending on
the app, just a redundant layer of complication, since the
basic shared library dynamic linking does it all.
In short, transferring Java patterns uncritically to C++ isn't
a good idea.
They're two different languages, and in particular in this
case, with ordinary C++ you build a different executable for
each platform (link time selection is a good idea), while with
Java you may build a common executable for all platforms (run
time selection, implying a factory, is a good idea).

In Java, if you have code accessing specific system level API's
(e.g. the registry under Windows) which aren't addressed by the
standard library, the only solution available to you is to
create a native class, with the implementation in some other
language (like C++); in such cases, it is usual to provide
platform specific versions, each with the appropriate dynamic
object containing the native code.
 
A

Alf P. Steinbach

* James Kanze:
I'd be interested in hearing how. It's the standard technique,
and I've not found it to cause any particular problems for the
build or for testing.



It's also a C++ technique. I think what you meant to say is
that it's the only technique available in Java, so people used
to Java end up using in even in cases like this where it is
totally inappropriate. (When all you have is a hammer,
everything looks like a nail.)
Yes.



In C++ as it is actually practiced, there are, and it's actually
common practice to use something like this, e.g. loading a
different look and feel for a GUI according to an environment
variable.

A better example than *nix GUI might be Windows COM object instantiation. :)

Since you're going to work in Windows, check out CoCreateInstance & friends.

It's the factory pattern taken to the extreme, but unfortunately, while designed
primarily *for* C++, it's implemented in C style (it also supports use from C).

Depending on what the different versions do, the code for one
platform might not even compile on another.

Right. That's part of why it's impractical.

In Java, if you have code accessing specific system level API's
(e.g. the registry under Windows) which aren't addressed by the
standard library, the only solution available to you is to
create a native class, with the implementation in some other
language (like C++); in such cases, it is usual to provide
platform specific versions, each with the appropriate dynamic
object containing the native code.

Technically yes, the machine-specific things are necessarily machine-specific
and thus different, heh. I assumed that to be understood. You're forgetting that
in Java the main program can be and usually is the same on all platforms, at the
bytecode level, while with C++ it typically has hardcoded the shared library
usage, different on each platform. You *can* do about the same in C++, explitly
loading a shared library at run-time, and wasting time on writing small wrappers
or casts for invoking each relevant routine. But that won't get you a machine
code level identical executable for different systems, and unless the design is
royally screwed up it won't even make the source code more system independent,
and happily there's seldom any need to add this extra layer. :)


Cheers,

- Alf
 
T

Tech07

Alf said:
* Tech07:

That was an utterance in context, as is everything here.

When you "jump in", read the thread first.

And before you "jump in", try to get some familiarity with Usenet in
general, say for some weeks (not just this group, although that's
recommended:
see the FAQ).


You're saying, *because* of your limited ability to read and
understand (demonstrated above), I should try to convey "esoteric
things"?
Don't you think you'd have even more trouble with esoteric things
than with fundamental, basic, absolute beginner's things like the
above?

Your tantrum noted.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,735
Latest member
HikmatRamazanov

Latest Threads

Top