Reputation: 7867
For example, if I have so many class that have same prefix in same platform:
in android:
Printer *p=new AndroidPrinter();
Writer *w=new AndroidWriter();
Connector *c=new AndroidConnector();
in iOS:
Printer *p=new IOSPrinter();
Writer *w=new IOSWriter();
Connector *c=new IOSConnector();
is it possible to define a part of class name like that:
#define PREFIX Android
int main(){
Printer *p=new PREFIXPrinter();
Writer *w=new PREFIXWriter();
Connector *c=new PREFIXConnector();
return 0;
}
instead of:
#define PLATFORM 0
#if PLATFORM==0
#define PRINTER AndroidPrinter
#else
#define PRINTER IOSPrinter
#endif
#if PLATFORM==0
#define WRITER AndroidWriter
#else
#define WRITER IOSWriter
#endif
#if PLATFORM==0
#define CONNECTOR AndroidConnector
#else
#define CONNECTOR IOSConnector
#endif
int main(){
Printer *p=new PRINTER();
Writer *w=new WRITER();
Connector *c=new CONNECTOR();
return 0;
}
Upvotes: 16
Views: 4604
Reputation: 22448
You can't. PREFIXPrinter();
will be a function named PREFIXPrinter();
not AndroidPrinter();
You can do it like this:
#define PREFIX(a) Android##a
Usage:
PREFIX(Printer)();
It will call AndroidPrinter()
Upvotes: 9
Reputation: 10007
You can not have exactly the syntax you want. However, you can do some more preprocessor magic to have it working:
#define CONCAT_HELPER(a,b) a ## b
#define CONCAT(a,b) CONCAT_HELPER(a,b)
#define x ABC
#define MAKENAME(y) CONCAT(x,y)
int MAKENAME(def); // this defines ABCdef variable
int main() {
ABCdef = 0;
return 0;
}
However, it would be a better way to use another approach, such as namespace suggested in the comments, or even better an abstract factory, something like this:
class Factory {
public:
virtual Printer* getPrinter() = 0;
virtual Writer* getWriter() = 0;
};
class AndroidFactory: public Factory {
public:
virtual Printer* getPrinter() { return new AndroidPrinter(); }
virtual Writer* getWriter() { return new AndroidWriter(); }
};
// the same for IOS, or,
// if you really have such a similar code here,
// you can make a macros to define a factory
...
int main() {
#ifdef ANDROID
Factory* factory = new AndroidFactory();
#else
Factory* factory = new IOSFactory();
#endif
// now just pass factory pointer everywhere
doSomething(old, parameters, factory);
}
(Even better will be to use auto_ptr
s or even unique_ptr
s.)
(You can also make your factory a singleton if you want.)
UPDATE:
Another approach is to have two independent factory classes and just typedef
to use one of them:
class AndroidFactory { // no inheritance here
public:
Printer* getPrinter() {...} // no virtual here!
...
};
class IOSFactory {
...
};
#ifdef ANDROID
typedef AndroidFactory Factory;
#else
typedef IOSFactory Factory;
#endif
// note that we pass Factory* here
// so it will compile and work on both Android and IOS
void doSomething(int param, Factory* factory);
int main() {
Factory* factory = new Factory();
// and pass factory pointer around
doSomething(param, factory);
}
See also TartanLlama's answer for an more advanced version of this approach, including static functions to avoid passing a pointer around.
This approach has an advantage that everything is resolved at compile time and no virtual calls are made.
However, I do not think that virtual functions will be a real bottleneck, because being a factory it will not be called many times. I think your usage pattern will be something on the lines of
Foo* foo = factory.getFoo();
foo->doSomething();
and most of the time will be spend in the doSomething()
call, so getFoo()
virtual call overhead will not be a bottleneck.
At the same time, this approach has a disadvantage that it lacks proper inheritance structure and thus makes extension more difficult (and static functions would make extension even more difficult).
Imagine you will want to have a bit different factories for Android phones and Android tablets. With inheritance, you will simply have a basic AndroidBaseFactory
class that has common logic and two subclasses for phones and tablets replacing only what you need. Without inheritance you will have to copy all common functionality (even if it is just a return new AndroidFoo();
call) in both factory classes.
Also, unit testing and mocking is simpler with inheritance and pointers being passes around.
In fact, many of arguments of "singleton vs a bunch of static functions" apply here too.
Upvotes: 11
Reputation: 65770
You could use a static factory pattern with some template specialization for this. You don't really need a fully-fledged abstract factory, because you won't be switching platform halfway through execution. This way, you can work out which factory implementation to use at compile-time with no overhead (assuming that your factory methods are inlined and just forward to operator new
).
Define a couple of dummy structs to act as platform identifiers:
struct Android{};
struct IOS{};
Write some factory implementations based on those dummy structs:
template <typename T>
struct ConcreteComponentFactory;
template <>
struct ConcreteComponentFactory<Android>
{
static Printer *CreatePrinter()
{ return new AndroidPrinter(); }
static Writer *CreateWriter()
{ return new AndroidWriter(); }
static Connector *CreateConnector()
{ return new AndroidConnector(); }
};
template <>
struct ConcreteComponentFactory<IOS>
{
static Printer *CreatePrinter()
{ return new IOSPrinter(); }
static Writer *CreateWriter()
{ return new IOSWriter(); }
static Connector *CreateConnector()
{ return new IOSConnector(); }
};
Work out what platform to use based on some macro:
#define PLATFORM 0
#if PLATFORM==0
using Platform = Android;
#else
using Platform = IOS;
#endif
Then typedef the instance for the platform you are working on:
using ComponentFactory = ConcreteComponentFactory<Platform>;
Now we can create instances of our objects and forget about what platform we are on:
Writer *a = ComponentFactory::CreateWriter();
If we need to know what platform we are on at some point, we can just refer to the Platform
typedef.
This approach is pretty flexible and far more type-safe than the macro-based version you have.
Upvotes: 18
Reputation: 77073
This is not possible, but you can do other things with similar generalizing effect.
As @Biffen pointed out, you can use namespaces
As @Petr pointed out, you can use an abstract factory
As @Petr pointed out in his answer, you can do some preprocessor magic
You can use reflection
Upvotes: 1