tags:

views:

155

answers:

5

Consider the following template class

class MyClassInterface {
public:
  virtual double foo(double) = 0;
}

class MyClass<int P1, int P2, int P3>
: public MyClassInterface {
public:
  double foo(double a) {
    // complex computation dependent on P1, P2, P3
  }
  // more methods and fields (dependent on P1, P2, P3)
}

The template parameters P1, P2, P3 are in a restricted range like from 0 to some fixed value n fixed at compile time.

Now I would like to build a "factory" method like

MyClassInterface* Factor(int p1, int p2, int p3) {
  return new MyClass<p1,p2,p3>(); // <- how to do this?
}

The question would be how to achieve the construction of the template class when template parameters are only known at runtime. And would the same be possible with template parameters having a very large domain (like a double)? Please consider also, if the possible solution is extandable to using more template parameters.

+4  A: 

Thats not posible, templates are instantiated at compile time.
By the time you have an executable you only have classes(particular instantiations of those templates), no templates any more.

If you don't know values at compile time you can't have templates for those.

Arkaitz Jimenez
This is not true. For integer parameters with a small domain, one can use switch/if statements, as indicated in the post by sharth.
Danvil
There is no way of `achieve the construction of the template class when template parameters are only known at runtime` without constructing all the possible cases and doing runtime switching.
Arkaitz Jimenez
And the question was how to for example do this recursive switching (without writing it out in code by hand).
Danvil
A: 

You can't. template are compile time only.

You can build at compile time all the possible templates values you want, and choose one of them in run time.

brickner
+6  A: 

Here's what you can do:

MyClassInterface* Factor(int p1, int p2, int p3) {
  if (p1 == 0 && p2 == 0 && p3 == 0)
    return new MyClass<0,0,0>();
  if (p1 == 0 && p2 == 0 && p3 == 1)
    return new MyClass<0,0,1>();
  etc;
}

Note that this does not even remotely scale to floating point values. It scales only to a known list of discrete values.


I've also used this bit of code before to do some template automatic generation:

#include <boost/preprocessor.hpp>

#define RANGE ((0)(1)(2)(3)(4)(5)(6)(7)(8)(9)(10)(11)(12))
#define MACRO(r, p) \
    if (BOOST_PP_SEQ_ELEM(0, p) == var1 && BOOST_PP_SEQ_ELEM(1, p) == var2 && BOOST_PP_SEQ_ELEM(2, p) == var3 && BOOST_PP_SEQ_ELEM(3, p) == var4) \
        actual_foo = foo<BOOST_PP_TUPLE_REM_CTOR(4, BOOST_PP_SEQ_TO_TUPLE(p))>;
BOOST_PP_SEQ_FOR_EACH_PRODUCT(MACRO, RANGE RANGE RANGE RANGE)
#undef MACRO
#undef RANGE

The compiler produces output that looks like this:

if (0 == var1 && 0 == var2 && 0 == var3 && 0 == var4) actual_foo = foo<0, 0, 0, 0>;
if (0 == var1 && 0 == var2 && 0 == var3 && 1 == var4) actual_foo = foo<0, 0, 0, 1>;
if (0 == var1 && 0 == var2 && 0 == var3 && 2 == var4) actual_foo = foo<0, 0, 0, 2>;
if (0 == var1 && 0 == var2 && 0 == var3 && 3 == var4) actual_foo = foo<0, 0, 0, 3>;
if (0 == var1 && 0 == var2 && 0 == var3 && 4 == var4) actual_foo = foo<0, 0, 0, 4>; 
if (0 == var1 && 0 == var2 && 0 == var3 && 5 == var4) actual_foo = foo<0, 0, 0, 5>;
if (0 == var1 && 0 == var2 && 0 == var3 && 6 == var4) actual_foo = foo<0, 0, 0, 6>;
if (0 == var1 && 0 == var2 && 0 == var3 && 7 == var4) actual_foo = foo<0, 0, 0, 7>;
if (0 == var1 && 0 == var2 && 0 == var3 && 8 == var4) actual_foo = foo<0, 0, 0, 8>;
etc...

Also, please note that with this method, with 4 variables, each ranging over 13 values, You would cause the compiler to instantiate 28561 copies of this function. If your n was 50, and you still had 4 options, you would have 6250000 functions instantiated. This can make for a SLOW compile.

sharth
Thanks, your answer is really helpful!
Danvil
'This can make for a SLOW compile' - Not to mention a preprocessed size approaching half a gigabyte and a massive resulting executable size if you ever found a compiler that could cope with that.
Joe Gauterin
@Joe: Absolutely. I've used this for a set of bools that I wanted to try templating out. I think the most I ever did was < 100 generated instantiations.
sharth
Why didn't you use `BOOST_PP_SEQ_ENUM_PARAMS` instead of converting to tuple and removing the constructor ? Is there an efficiency reason ?
Matthieu M.
@Matthieu: The efficiency of the macro generation isn't really an issue. I never used this code for more than a day, so it was just hacked together. The only other comment would be that I was using Boost 1.33.1 which may or may not support some additional macro calls.
sharth
@sharth What makes you think that floating point values aren't discrete? There are even less float values than there are int values, because NaN and co. have some duplicate representations.
FredOverflow
@Fred: I'm more referring to the fact that if your range is [0, 10], it's a lot easier to do that if you are using ints, rather than floats. It's not that there are not a finite number of floats in that range, it's that there are far too many.
sharth
+2  A: 

It is technically possible* - but it's not practical and it's almost certainly the wrong way to approach the problem.

Is there some reason why P1, P2 and P3 can't be regular integer variables?


*You could embed a C++ compiler and a copy of your source, then compile a dynamic library or shared object that implements your factory function for a given set of P1,P2,P3 - but do you really want to do that? IMO, that's an absolutely crazy thing to be doing.

Joe Gauterin
+1  A: 

If macros aren't your thing then you can also generate the if-then-else's using templates:

#include <stdexcept>
#include <iostream>

const unsigned int END_VAL = 10;

class MyClassInterface
{
public:
    virtual double foo (double) = 0;
};

template<int P1, int P2, int P3>
class MyClass : public MyClassInterface
{
public:
    double foo (double a)
    {
        return P1 * 100 + P2 * 10 + P3 + a;
    }
};

struct ThrowError
{
    static inline MyClassInterface* create (int c1, int c2, int c3)
    {
        throw std::runtime_error ("Could not create MyClass");
    }
};

template<int DEPTH = 0, int N1 = 0, int N2 = 0, int N3 = 0>
struct Factory : ThrowError {};

template<int N2, int N3>
struct Factory<0, END_VAL, N2, N3> : ThrowError {};

template<int N1, int N3>
struct Factory<1, N1, END_VAL, N3> : ThrowError {};

template<int N1, int N2>
struct Factory<2, N1, N2, END_VAL> : ThrowError {};

template<int N1, int N2, int N3>
struct Factory<0, N1, N2, N3>
{
    static inline MyClassInterface* create (int c1, int c2, int c3)
    {
        if (c1 == N1)
        {
            return Factory<1, N1, 0, 0>::create (c1, c2, c3);
        }
        else
            return Factory<0, N1 + 1, N2, N3>::create (c1, c2, c3);
    }
};

template<int N1, int N2, int N3>
struct Factory<1, N1, N2, N3>
{
    static inline MyClassInterface* create (int c1, int c2, int c3)
    {
        if (c2 == N2)
        {
            return Factory<2, N1, N2, 0>::create (c1, c2, c3);
        }
        else
            return Factory<1, N1, N2 + 1, N3>::create (c1, c2, c3);
    }
};

template<int N1, int N2, int N3>
struct Factory<2, N1, N2, N3>
{
    static inline MyClassInterface* create (int c1, int c2, int c3)
    {
        if (c3 == N3)
        {
            return new MyClass<N1, N2, N3> ();
        }
        else
            return Factory<2, N1, N2, N3 + 1>::create (c1, c2, c3);
    }
};

MyClassInterface* factory (int c1, int c2, int c3)
{
    return Factory<>::create (c1, c2, c3);
}

Since the tests are nested it should be more efficient than sharth's macro solution.

You can extend it to more parameters by adding more depth cases.

jon hanson
This is tricky :D
Danvil
TMP often is...
jon hanson