Re: Mapping a non-const integral to a type
On Mar 27, 4:02 am, Marcos Bento <marcosbe...@gmail.com> wrote:
I'm having a bit of a problem with the mapping a non-const integral
to a type. The code below shows my predicament:
#include <iostream>
typedef enum { typeX, typeY, typeZ } Types;
class X {};
class Y {};
class Z {};
void f(const X& v) { std::cerr << "X" << std::endl; }
void f(const Y& v) { std::cerr << "Y" << std::endl; }
void f(const Z& v) { std::cerr << "Z" << std::endl; }
template <typename T>
void read(T& v, const char* file)
{}
int main()
{
Types user_choice = typeX; // Actually, this is taken from
configuration file - so non-constant integral!
const char *file = "input.txt";
switch( user_choice ) {
case typeX: {
X input;
::read(input, file);
}
break;
case typeY: {
Y input;
::read(input, file);
}
break;
case typeZ: {
Z input;
::read(input, file);
}
break;
default: {}
}
::f(input); // Obviously, input has not been declared at this
scope!!! :-(
}
You have several choices here, depending on whether you can modify
f(), use the heap or do some refactoring.
1) Move the f() call inside the cases. There's also other schemes like
constructing all three objects and keeping a flag to say which one
you're using. All of this sucks for various reasons you seem to
already be aware of, but it may be your only choice.
2) Have X, Y and Z inherit from a base class B and create the right
object on the heap. This will allow you to get 'input' out of the
switch. You could also move the whole switch to a different
function and return a B*. You can then make f() a virtual function.
If you can't modify f(), make a trampoline virtual function
call_f(), implement it in all derived classes and call f(*this).
3) If you can't modify X, Y, Z or f(), then wrap them in a class of
your own:
class wrapper
{
public:
virtual void call_f() = 0;
};
template <class T>
class concrete_wrapper : public wrapper
{
public:
concrete_wrapper(const char* file)
{
::read(input_, file);
}
virtual void call_f()
{
::f(input_);
}
private:
T input_;
};
typedef std::auto_ptr<wrapper> wrapper_ptr;
wrapper_ptr create_wrapper(Types t, const char* file)
{
switch (t)
{
case typeX:
return wrapper_ptr(new concrete_wrapper<X>(file));
case typeY:
return wrapper_ptr(new concrete_wrapper<Y>(file));
//..
}
return wrapper_ptr();
}
void frob(Types t, const char* file)
{
wrapper_ptr w = create_wrapper(t, file);
w->call_f();
}
If 2) and 3) are not good enough because of inheritance or the heap,
try those:
3) If the code inside the cases is somewhat equivalent, move that to
a templated function:
template <class T>
void frob(const char* file)
{
T input;
::read(input, file);
::f(input);
}
int main()
{
Types user_choice = typeX;
const char *file = "input.txt";
switch( user_choice )
{
case typeX:
{
frob<X>(file);
break;
}
case typeY:
{
frob<Y>(file);
break;
}
//..
}
}
4) If the code inside the cases is different, but what follows is
common, try this:
template <class T>
void twiddle(T& input)
{
::f(input);
// and all common code for X, Y and Z
}
template <class T>
void frob(const char* file);
template <>
void frob<X>(const char* file)
{
X input;
::read(input, file);
// do whatever is specific to X
twiddle(input);
}
template <>
void frob<Y>(const char* file)
{
Y input;
::read(input, file);
// do whatever is specific to Y
twiddle(input);
}
// same for Z
int main()
{
Types user_choice = typeX;
const char *file = "input.txt";
switch (user_choice)
{
case typeX:
{
frob<X>(file);
break;
}
case typeY:
{
frob<Y>(file);
break;
}
// same for Z
}
}
Can't think of anything else right now.
--
Jonathan Mcdougall
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]