Elegant way of making template arguments dependent on user input?

T

Thomas Pajor

Hey everybody,


I got into serious trouble with template programming. I have a class
which uses three template arguments, say


template<typename Atype, typename Btype, typename Ctype>
class some_class {
};


and I want to make the values of these template arguments dependent on
the input of the program as shown by the following pseudocode:


if (input_for_Atype == "a")
firsttype := first_A;
else if (input_for_Atype == "b")
firsttype := second_A;
//...

if (input_for_Btype == "1")
secondtype := first_B;
else if (input_for_Btype == "2")
secondtype := second_B;
//...

// analogous for Ctype

// instanciate correct version of "some_class"
some_class<firsttype, secondtype, thirdtype> instance_of_class;

// Begin doing stuff with the class
instance_of_class.do_something();
//...
// End


I hope you get the idea.

The number of choices for each template argument is limited of course,
so a canonical solution would be to write out each possible combination
of the template arguments. This is really ugly, since in my case there
are already 24 possible combinations (2*4*3) for the template arguments,
and there will most likely be more in the future. Also the code would be
redundant, as the part between "Begin doing stuff" and "end" is always
the same for each of the combinations.

My question is whether there is a more elegant way to solve my problem.

I have already tried to do the following. For each of the template
arguments write a function which decides (dependant on the input) the
type of its related template, like this:


template<typename Atype, typename Btype, typename Ctype>
inline void run() {
some_class<Atype, BType, Ctype> instance_of_class();
// do the stuff... code is only written once!
}

template<typename Atype, typename Btype>
inline void choose_C() {
if (input_for_Ctype == 1)
run<Atype, Btype, first_C>();
else if (input_for_Ctype == 2)
run<Atype, Btype, second_C>();
// ...
}

template<typename Atype>
inline void choose_B() {
if (input_for_Btype == 1)
choose_C<Atype, first_B>();
else if (input_for_Btype == 2)
choose_C<Atype, second_B>();
//...
}

inline void choose_A() {
if (input_for_Atype == 1)
choose_B<first_A>();
else
choose_B<second_A>();
}

int main() {
choose_A();
}


Unfortunately this won't work in my case, since the value of the Atype
template also depends on "input_for_Ctype". In fact, the method choose_A
looks like this:


inline void choose_A() {
if (input_for_Atype == 1 || input_for_Ctype == 1)
choose_B<first_A>();
else
choose_B<second_A>();
}


Now, it is crucial for input_for_Ctype == 1 to use first_A as the Atype,
since it defines some member variables which aren't available in
second_A. Unfortunately my C++ compiler seems to compile the code in the
run method for Atype = second_A and Ctype = first_C as well, though this
path is never taken in the program. This is leading to errors that
second_A won't have the requested members. Declaring all functions
inline as shown above didn't help.


I hope this wasn't too much at once, but the situation seems to be
rather complex. If there are any questions to the problem just feel free
to ask them and I will try to explain it in more detail.


Is there any solution for this or is it just impossible in C++? Of
course an alternative would be to do it with class inheritance and
virtual methods, but this is no option, since performance is most
important (I'm testing algorithms on huge datasets, taking hours of time
already). "Googling" on this problem didn't really reveal anything of use.


Greets and thanks in advance,
Thomas



PS: Maybe this is of use:

# gcc -v
Using built-in specs.
Target: x86_64-suse-linux
Configured with: ../configure --enable-threads=posix --prefix=/usr
--with-local-prefix=/usr/local --infodir=/usr/share/info
--mandir=/usr/share/man --libdir=/usr/lib64 --libexecdir=/usr/lib64
--enable-languages=c,c++,objc,fortran,java,ada --enable-checking=release
--with-gxx-include-dir=/usr/include/c++/4.1.0 --enable-ssp
--disable-libssp --enable-java-awt=gtk --enable-gtk-cairo
--disable-libjava-multilib --with-slibdir=/lib64 --with-system-zlib
--enable-shared --enable-__cxa_atexit --enable-libstdcxx-allocator=new
--without-system-libunwind --with-cpu=generic --host=x86_64-suse-linux
Thread model: posix
gcc version 4.1.0 (SUSE Linux)


# uname -a
Linux compute6 2.6.16.13-4-smp #1 SMP Wed May 3 04:53:23 UTC 2006 x86_64
x86_64 x86_64 GNU/Linux
 
G

Gianni Mariani

Thomas Pajor wrote:
....
Is there any solution for this or is it just impossible in C++? Of
course an alternative would be to do it with class inheritance and
virtual methods, but this is no option, since performance is most
important (I'm testing algorithms on huge datasets, taking hours of time
already). "Googling" on this problem didn't really reveal anything of use.

You really need to check to see if performance is an issue before you go
spending an effort on it.

If it is user input, you need to instantiate every case possible of your
template. To make it selectable, a base class or a pointer to a
function with the same signature is needed, I prefer an abstract base class.

The most extensible system (no switch or if's) is a generalized factory
system like the one in Austria C++.

Once all classes have been registered with the factory you can simply

Base * p = at::FactoryRegister< Base, Key >( Key( A, B, C ) )();

Any way you look at it, you need to instantiate all the possible types
you care about.
 
G

Guest

Hey everybody,


I got into serious trouble with template programming. I have a class
which uses three template arguments, say


template<typename Atype, typename Btype, typename Ctype>
class some_class {
};


and I want to make the values of these template arguments dependent on
the input of the program as shown by the following pseudocode:

Templates are a compile-time construct. You can not use any information
that is not known at compile-time when instantiating them.
 
T

Thomas Pajor

Gianni said:
You really need to check to see if performance is an issue before you go
spending an effort on it.

Unfortunately, performance is the main issue, but I have to admit, that
we didn't test it with virtual methods. But anyway the whole framework
would need massive rewriting in order to make it based on virtual
methods and class hierarchies.
If it is user input, you need to instantiate every case possible of your
template. To make it selectable, a base class or a pointer to a
function with the same signature is needed, I prefer an abstract base
class.

The most extensible system (no switch or if's) is a generalized factory
system like the one in Austria C++.

Once all classes have been registered with the factory you can simply

Base * p = at::FactoryRegister< Base, Key >( Key( A, B, C ) )();

Any way you look at it, you need to instantiate all the possible types
you care about.

Okay, thanks, I will look into this. I just thought there might be a
way to do it with more elegance. Maybe I will focus on making all three
template arguments fully orthogonal instead, so they can be combined in
every way. Then my approach with the "choose functions" should also work.

- Thomas
 
T

Thomas Pajor

Erik said:
> Templates are a compile-time construct. You can not use any information
> that is not known at compile-time when instantiating them.
>

I'm well aware of this, but I thought the compiler could use the limited
amount of available choices for each template argument to generate all
neccessary versions of the class and instanciate the correct one
depending on the user input.

Basically I was looking for a way to shorten code like this


if (input_choice_A == 1) {
if (input_choice_B == 1) {
some_class<firstA,firstB> instance();
// redundant code
} else {
some_class<firstA,secondB> instance();
// redundant code
}
} else {
if (input_choice_B == 1) {
some_class<secondA,firstB> instance();
// redundant code
} else {
some_class<secondA,secondB> instance();
// redundant code
}
}


Of course, instead of inserting the redundant code each time, you could
write a templated function like this:


template<typename Atype, typename Btype>
void run(some_class<Atype, Btype> &instance) {
// Redundant code here only once
}


So it seems to be technically possible to make template values dependent
on user input. But - and this is what really concerns me - I'd need to
define each combination of template arguments manually, which is a LOT
in my case. It would be much shorter (and easier maintainable) if I
could define them in a way as illustrated here


if (input_choice_A == 1)
typedef firstA first_argument; // I know this won't be visible from
outside the if construct
else
typedef secondA first_argument;

if (input_choice_B == 1)
typedef firstB second_argument;
else
typedef secondB second_argument;


And then just instanciate the class with


some_class<first_argument, second_argument> instance();


The advantage of such a technique becomes more and more obvious the more
combinations for the template arguments there are.


Regards,
Thomas
 
R

robspinella

I'm well aware of this, but I thought the compiler could use the limited
amount of available choices for each template argument to generate all
neccessary versions of the class and instanciate the correct one
depending on the user input.

Basically I was looking for a way to shorten code like this

if (input_choice_A == 1) {
if (input_choice_B == 1) {
some_class<firstA,firstB> instance();
// redundant code
} else {
some_class<firstA,secondB> instance();
// redundant code
}} else {

if (input_choice_B == 1) {
some_class<secondA,firstB> instance();
// redundant code
} else {
some_class<secondA,secondB> instance();
// redundant code
}

}

Of course, instead of inserting the redundant code each time, you could
write a templated function like this:

template<typename Atype, typename Btype>
void run(some_class<Atype, Btype> &instance) {
// Redundant code here only once

}

So it seems to be technically possible to make template values dependent
on user input. But - and this is what really concerns me - I'd need to
define each combination of template arguments manually, which is a LOT
in my case. It would be much shorter (and easier maintainable) if I
could define them in a way as illustrated here

if (input_choice_A == 1)
typedef firstA first_argument; // I know this won't be visible from
outside the if construct
else
typedef secondA first_argument;

if (input_choice_B == 1)
typedef firstB second_argument;
else
typedef secondB second_argument;

And then just instanciate the class with

some_class<first_argument, second_argument> instance();

The advantage of such a technique becomes more and more obvious the more
combinations for the template arguments there are.

Regards,
Thomas

You should check out boost::mpl (http://www.boost.org/libs/mpl/doc/
index.html). You could use it to statically configure typelists of
allowed types for template parameters A,B and C and write a template
metaprogram to do the heavy lifting of enumerating the possibilities
at compile time. You would need to combine this with a factory (see
Alexandresceu's Modern C++ Design for a nice implementation) in order
to retrieve the types by name and make use of the enumerated types.

The problem that you will run into (and that has been mentioned
already) is knowing what type to declare for the object returned by
your factory. From the perspective of the caller, in your case the
factory returns an object of an indeterminate templated type fetched
by name. You will need to use inheritance and provide an interface in
order to declare and manipulate these objects.

Note well that the time it takes for the compiler to enumerate the
template possibilities (by construction of your approach) is
factorially complex and will, almost certainly, not result in swift
compile times. It will scale horribly with the addition of new types.

rob.
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top