Re: case/switch metaprogramming
Aaron Graham wrote:
On Jul 24, 3:56 pm, "Victor Bazarov" <v.Abaza...@comAcast.net> wrote:
Aaron Graham wrote:
I have a switch/case statement that looks something like this:
std::auto_ptr<Dev> dev;
switch (dev_id) {
case 1: dev = std::auto_ptr<Dev>(new devices::Device<1>()); break;
case 2: dev = std::auto_ptr<Dev>(new devices::Device<2>()); break;
case 3: dev = std::auto_ptr<Dev>(new devices::Device<3>()); break;
case 4: dev = std::auto_ptr<Dev>(new devices::Device<4>()); break;
case 5: dev = std::auto_ptr<Dev>(new devices::Device<5>()); break;
....
case 47: dev = std::auto_ptr<Dev>(new devices::Device<47>()); break;
default: dev = std::auto_ptr<Dev>(new devices::Device<0>()); break;
}
Note that the switch can grow arbitrarily large and is very
repetitive. Is there an easy way to use metaprogramming to get the
compiler to generate all this code?
Yes, I believe so. Check this out:
// --------------------- this is just to make it compile
#include <iostream>
class Dev {
public:
virtual ~Dev() {}
};
template<unsigned id> class Device : public Dev {
static const unsigned s_id = id;
public:
Device() { std::cout << "Creating Device<" << id << ">\n"; }
};
// --------------------- here begins the important part:
// presuming creating all devices between 1 and 'hi_id'
template<unsigned hi_id>
Dev* getDeviceFrom(unsigned dev_id) {
if (dev_id > hi_id || dev_id == 0) // here is your 'default:'
return new Device<0>;
if (dev_id == hi_id)
return new Device<hi_id>; // Bingo!
else // dev_id < hi_id
return getDeviceFrom<hi_id-1>(dev_id);
}
template<>
Dev* getDeviceFrom<1>(unsigned i) {
return new Device<1>();
}
// -------------------------------------------------------
#include <time.h>
int main()
{
srand((unsigned)time(0));
Dev *p[10];
for (int i = 0; i < 10; ++i) {
// here instead of the 'switch' I have
unsigned dev_id = rand() % 20; // or whatever
p[i] = getDeviceFrom<30>(dev_id); // out of 30
}
for (int i = 0; i < 10; ++i)
delete p[i];
}
The code is recursive, and you will possibly run quite soon out of
compiler resources (just like with other recursive templates), so
please adjust the total number of devices (here I have 30) to your
bare minimum.
Enjoy!
Very nice. One improvement on that: I can get the same effect without
the special "default" case in the generalized version of the function:
template<unsigned hi_id>
Dev* getDeviceFrom(unsigned dev_id) {
if (dev_id == hi_id)
return new Device<hi_id>; // Bingo!
return getDeviceFrom<hi_id-1>(dev_id);
This is significantly different semantically from your 'switch'
statement. If 'dev_id' is _greater_ than the highest number, the
'switch' statement will go into 'default', but in this case it
will create the "Device" with the highest 'id'. I don't think
that that's what you want.
}
template<>
Dev* getDeviceFrom<0>(unsigned i) {
return new Device<0>(); // default case here
}
That should generate less code.
With different semantics.
Now I'm trying to figure out a way to get it to "skip over" devices
that aren't defined, to fall back on the default case. For instance,
if Device<12> isn't defined, I want a Device<0>, but this
implementation will give me a compile time error instead.
Essentially, I need the compiler to elide the "if" clause for
getDeviceFrom<12> when it finds that type is not declared.
That's relatively simple. Here is another indirection -- to
calculate the type to 'new' depending on whether to skip it or
not:
template<unsigned id_> struct skip_id { enum { yes = 0 }; };
template<unsigned id> struct which_Device {
typedef Device< skip_id<id>::yes ? 0 : id > Device;
};
template<unsigned hi_id>
Dev* getDeviceFrom(unsigned dev_id) {
if (dev_id > hi_id || dev_id == 0) // here is your 'default:'
return new Device<0>;
if (dev_id == hi_id)
return new which_Device<hi_id>::Device; // Bingo!
else // dev_id < hi_id
return getDeviceFrom<hi_id - 1>(dev_id);
}
template<>
Dev* getDeviceFrom<1>(unsigned i) {
if (skip_id<1>::yes)
return new Device<0>();
else
return new Device<1>();
}
// how, to skip 12, 43, and 12345:
template<> struct skip_id<12> { enum { yes = 1 }; };
template<> struct skip_id<27> { enum { yes = 1 }; };
template<> struct skip_id<12345> { enum { yes = 1 }; };
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]