在运行时指定模板参数

时间:2010-05-20 12:48:54

标签: c++ templates

考虑以下模板类

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

模板参数P1P2P3处于受限制的范围内,例如从0到编译时固定的某个固定值n。< / p>

现在我想构建像

这样的“工厂”方法
MyClassInterface* Factor(int p1, int p2, int p3) {
  return new MyClass<p1,p2,p3>(); // <- how to do this?
}

问题是如何在模板参数仅在运行时知道时如何实现模板类的构造。对于具有非常大的域(如双)的模板参数,同样可能吗?如果可能的解决方案可扩展到使用更多模板参数,请另外考虑。

7 个答案:

答案 0 :(得分:17)

以下是您可以做的事情:

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编译。

答案 1 :(得分:10)

如果宏不是你的东西那么你也可以使用模板生成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的宏解决方案更有效。

您可以通过添加更多深度案例将其扩展到更多参数。

答案 2 :(得分:8)

这不可行,模板在编译时被实例化 当你有一个可执行文件时,你只有类(这些模板的特定实例),不再有模板。

如果您在编译时不知道值,则无法获得这些值。

答案 3 :(得分:2)

技术上可行** - 但它不实用,几乎肯定是解决问题的错误方法。

为什么P1,P2和P3不能成为常规整数变量?


*您可以嵌入C ++编译器和源代码的副本,然后编译动态库或共享对象,为给定的P1,P2,P3集合实现工厂函数 - 但您真的想这样做吗?国际海事组织,这是一件非常疯狂的事情。

答案 4 :(得分:2)

我不知道这是否适用于您当前的问题,但它似乎是C ++ 11 constexpr可能是您正在寻找的 - constexpr函数可以在运行时调用,同时可以在编译时执行。

使用constexpr还有一个额外的好处,即远离&#34;更清洁&#34;看起来比使用TMP,使用任何运行时值(不仅仅是整数值),同时保留了TMP的大部分好处,例如memoization和编译时执行,尽管这在一定程度上取决于编译器的决定。实际上,constexpr通常比TMP等效版本快得多。

另请注意,通常在运行时使用模板会破坏模板的最大功能之一 - 它们在编译期间处理并在运行时几乎消失。

答案 5 :(得分:1)

你做不到。模板只是编译时间。

您可以在编译时构建所需的所有模板值,并在运行时选择其中一个。

答案 6 :(得分:0)

太迟了,我知道,但是这个怎么样:

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