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

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

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, зафиксированного во время компиляции.

Теперь я хотел бы создать метод "factory", например

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

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

Ответ 1

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

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 функций. Это может сделать для компиляции SLOW.

Ответ 2

Если макросы не являются вашей вещью, вы также можете сгенерировать 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);
}

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

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

Ответ 3

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

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

Ответ 4

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

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


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

Ответ 5

Я не знаю, применимо ли это к вашей текущей проблеме, но похоже, что С++ 11 constexpr может быть тем, что вы ищете - constexpr функции могут вызываться во время выполнения и в то же время могут выполняться во время компиляции.

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

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

Ответ 6

слишком поздно, я знаю, но как насчет этого:

// MSVC++ 2010 SP1 x86
// boost 1.53

#include <tuple>
#include <memory>
// test
#include <iostream>

#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
#include <boost/mpl/size.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/unpack_args.hpp>
#include <boost/mpl/apply.hpp>
// test
#include <boost/range/algorithm/for_each.hpp>

/*! \internal
 */
namespace detail
{
/*! \internal
 */
namespace runtime_template
{

/*! \internal
    fwd
 */
template <
    typename Template
    , typename Types
    , typename Map  // top level map iterator
    , typename LastMap  // top level map iterator
    , int Index
    , bool Done = std::is_same<Map, LastMap>::value
>
struct apply_recursive_t;

/*! \internal
    fwd
 */
template <
    typename Template
    , typename Types
    , typename Map  // top level map iterator
    , typename LastMap  // top level map iterator
    , typename First
    , typename Last
    , int Index
    , bool Enable = !std::is_same<First, Last>::value
>
struct apply_mapping_recursive_t;

/*! \internal
    run time compare key values + compile time push_back on \a Types
 */
template <
    typename Template
    , typename Types
    , typename Map  // top level map iterator
    , typename LastMap  // top level map iterator
    , typename First
    , typename Last
    , int Index // current argument
    , bool Enable /* = !std::is_same<First, Last>::value */
>
struct apply_mapping_recursive_t
{
    typedef void result_type;
    template <typename TypeIds, typename T>
    inline static void apply(const TypeIds& typeIds, T&& t)
    {   namespace mpl = boost::mpl;
        typedef typename mpl::deref<First>::type key_value_pair;
        typedef typename mpl::first<key_value_pair>::type typeId;   // mpl::int
        if (typeId::value == std::get<Index>(typeIds))
        {
            apply_recursive_t<
                Template
                , typename mpl::push_back<
                    Types
                    , typename mpl::second<key_value_pair>::type
                >::type
                , typename mpl::next<Map>::type
                , LastMap
                , Index + 1
            >::apply(typeIds, std::forward<T>(t));
        }
        else
        {
            apply_mapping_recursive_t<
                Template
                , Types
                , Map
                , LastMap
                , typename mpl::next<First>::type
                , Last
                , Index
            >::apply(typeIds, std::forward<T>(t));
        }
    }
};

/*! \internal
    mapping not found
    \note should never be invoked, but must compile
 */
template <
    typename Template
    , typename Types
    , typename Map  // top level map iterator
    , typename LastMap  // top level map iterator
    , typename First
    , typename Last
    , int Index
>
struct apply_mapping_recursive_t<
    Template
    , Types
    , Map
    , LastMap
    , First
    , Last
    , Index
    , false
>
{
    typedef void result_type;
    template <typename TypeIds, typename T>
    inline static void apply(const TypeIds& /* typeIds */, T&& /* t */)
    {
        BOOST_ASSERT(false);
    }
};

/*! \internal
    push_back on \a Types template types recursively
 */
template <
    typename Template
    , typename Types
    , typename Map  // top level map iterator
    , typename LastMap  // top level map iterator
    , int Index
    , bool Done /* = std::is_same<Map, LastMap>::value */
>
struct apply_recursive_t
{
    typedef void result_type;
    template <typename TypeIds, typename T>
    inline static void apply(const TypeIds& typeIds, T&& t)
    {   namespace mpl = boost::mpl;
        typedef typename mpl::deref<Map>::type Mapping; // [key;type] pair vector
        apply_mapping_recursive_t<
            Template
            , Types
            , Map
            , LastMap
            , typename mpl::begin<Mapping>::type
            , typename mpl::end<Mapping>::type
            , Index
        >::apply(typeIds, std::forward<T>(t));
    }
};

/*! \internal
    done! replace mpl placeholders of \a Template with the now complete \a Types
    and invoke result
 */
template <
    typename Template
    , typename Types
    , typename Map
    , typename LastMap
    , int Index
>
struct apply_recursive_t<
    Template
    , Types
    , Map
    , LastMap
    , Index
    , true
>
{
    typedef void result_type;
    template <typename TypeIds, typename T>
    inline static void apply(const TypeIds& /* typeIds */, T&& t)
    {   namespace mpl = boost::mpl;
        typename mpl::apply<
            mpl::unpack_args<Template>
            , Types
        >::type()(std::forward<T>(t));
    }
};

/*! \internal
    helper functor to be used with invoke_runtime_template()
    \note cool: mpl::apply works with nested placeholders types!
 */
template <typename Template>
struct make_runtime_template_t
{
    typedef void result_type;
    template <typename Base>
    inline void operator()(std::unique_ptr<Base>* base) const
    {
        base->reset(new Template());
    }
};

}   // namespace runtime_template
}   // namespace detail

/*! \brief runtime template parameter selection

    \param Template functor<_, ...> placeholder expression
    \param Maps mpl::vector<mpl::vector<mpl::pair<int, type>, ...>, ...>
    \param Types std::tuple<int, ...> type ids
    \param T functor argument type

    \note all permutations must be compilable (they will be compiled of course)
    \note compile time: O(n!) run time: O(n)

    \sa invoke_runtime_template()
    \author slow
 */
template <
    typename Template
    , typename Map
    , typename Types
    , typename T
>
inline void invoke_runtime_template(const Types& types, T&& t)
{   namespace mpl = boost::mpl;
    BOOST_STATIC_ASSERT(mpl::size<Map>::value == std::tuple_size<Types>::value);
    detail::runtime_template::apply_recursive_t<
        Template
        , mpl::vector<>
        , typename mpl::begin<Map>::type
        , typename mpl::end<Map>::type
        , 0
    >::apply(types, std::forward<T>(t));
}

/*! \sa invoke_runtime_template()
 */
template <
    typename Template
    , typename Map
    , typename Base
    , typename Types
>
inline void make_runtime_template(const Types& types, std::unique_ptr<Base>* base)
{
    invoke_runtime_template<
        detail::runtime_template::make_runtime_template_t<Template>
        , Map
    >(types, base);
}

/*! \overload
 */
template <
    typename Base
    , typename Template
    , typename Map
    , typename Types
>
inline std::unique_ptr<Base> make_runtime_template(const Types& types)
{
    std::unique_ptr<Base> result;

    make_runtime_template<Template, Map>(types, &result);
    return result;
}

////////////////////////////////////////////////////////////////////////////////

namespace mpl = boost::mpl;
using mpl::_;

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

template <int P1, int P2, int P3>
class MyClass
: public MyClassInterface {
public:
    double foo(double /*a*/) {
        // complex computation dependent on P1, P2, P3
        std::wcout << typeid(MyClass<P1, P2, P3>).name() << std::endl;
        return 42.0;
    }
    // more methods and fields (dependent on P1, P2, P3)
};

// wrapper for transforming types (mpl::int) to values
template <typename P1, typename P2, typename P3>
struct MyFactory
{
    inline void operator()(std::unique_ptr<MyClassInterface>* result) const
    {
        result->reset(new MyClass<P1::value, P2::value, P3::value>());
    }
};

template <int I>
struct MyConstant
    : boost::mpl::pair<
        boost::mpl::int_<I>
        , boost::mpl::int_<I>
    > {};

std::unique_ptr<MyClassInterface> Factor(const std::tuple<int, int, int>& constants) {
    typedef mpl::vector<
        MyConstant<0>
        , MyConstant<1>
        , MyConstant<2>
        , MyConstant<3>
        // ...
    > MyRange;
    std::unique_ptr<MyClassInterface> result;
    invoke_runtime_template<
        MyFactory<_, _, _>
        , mpl::vector<MyRange, MyRange, MyRange>
    >(constants, &result);
    return result;
}

int main(int /*argc*/, char* /*argv*/[])
{
    typedef std::tuple<int, int, int> Tuple;
    const Tuple Permutations[] =
    {
        std::make_tuple(0,      0,  0)
        , std::make_tuple(0,    0,  1)
        , std::make_tuple(0,    1,  0)
        , std::make_tuple(0,    1,  1)
        , std::make_tuple(1,    0,  0)
        , std::make_tuple(1,    2,  3)
        , std::make_tuple(1,    1,  0)
        , std::make_tuple(1,    1,  1)
        // ...
    };

    boost::for_each(Permutations, [](const Tuple& constants) { Factor(constants)->foo(42.0); });
    return 0;
}

Ответ 7

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

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