Укажите шаблонные параметры во времени выполнения

Рассмотрите следующий шаблонный класс

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)
}

Шаблонные параметры P1, P2, P3 находятся в ограниченном диапазоне как от 0 к некоторому фиксированному значению n зафиксированный во время компиляции.

Теперь я хотел бы создать метод "фабрики" как

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

Вопрос состоял бы в том, как достигнуть конструкции шаблонного класса, когда шаблонные параметры только известны во времени выполнения. И то же было бы возможно с шаблонными параметрами, имеющими очень большой домен (как двойное)? Рассмотрите также, если возможное решение является растяжимым к использованию большего количества шаблонных параметров.

31
задан Danvil 22 June 2013 в 11:18
поделиться

5 ответов

Вот что вы можете сделать:

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;
}

Обратите внимание, что это даже отдаленно не масштабируется для значений с плавающей точкой. Он масштабируется только по известному списку дискретных значений.


Я также использовал этот фрагмент кода раньше для автоматической генерации шаблонов:

#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

Компилятор выдает результат, который выглядит примерно так:

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...

Также, пожалуйста, обратите внимание, что при использовании этого метода, с 4 переменными, каждая из которых имеет 13 значений, вы заставите компилятор создать 28561 копию этой функции. Если бы ваше n было 50, и у вас все еще было 4 варианта, вы бы инстанцировали 6250000 функций. Это может привести к медленной компиляции.

19
ответ дан 27 November 2019 в 22:32
поделиться

Если макросы не для вас, вы также можете сгенерировать if-then-else с помощью шаблонов:

#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);
}

Поскольку тесты являются вложенными, это должно быть более эффективным, чем макрос решения Sharth.

Вы можете расширить его до большего количества параметров, добавив больше вариантов глубины.

11
ответ дан 27 November 2019 в 22:32
поделиться

Это невозможно, шаблоны создаются во время компиляции.
К тому времени, когда у вас будет исполняемый файл, у вас будут только классы (конкретные экземпляры этих шаблонов), больше никаких шаблонов.

Если вы не знаете значений во время компиляции, у вас не может быть для них шаблонов.

8
ответ дан 27 November 2019 в 22:32
поделиться

Вы не можете. шаблон только во время компиляции.

Вы можете создать во время компиляции все возможные значения шаблонов, которые хотите, и выбрать одно из них во время выполнения.

1
ответ дан 27 November 2019 в 22:32
поделиться

Технически это *возможно** - но это непрактично, и почти наверняка это неправильный подход к проблеме.

Есть ли какая-то причина, по которой P1, P2 и P3 не могут быть обычными целочисленными переменными?


* Вы могли бы встроить компилятор C++ и копию вашего исходного текста, затем скомпилировать динамическую библиотеку или разделяемый объект, реализующий вашу фабричную функцию для заданного набора P1, P2, P3 - но действительно ли вы хотите это делать? IMO, это абсолютно безумная вещь, которую нужно делать.

2
ответ дан 27 November 2019 в 22:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: