考虑以下模板类
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
。< / p>
现在我想构建像
这样的“工厂”方法MyClassInterface* Factor(int p1, int p2, int p3) {
return new MyClass<p1,p2,p3>(); // <- how to do this?
}
问题是如何在模板参数仅在运行时知道时如何实现模板类的构造。对于具有非常大的域(如双)的模板参数,同样可能吗?如果可能的解决方案可扩展到使用更多模板参数,请另外考虑。
答案 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;
}