我有一个由一些模板参数参数化的类:
template<typename Scalar, typename Integrator, int Dimension>
class foo;
每个模板参数可以是几种可能类型之一。目前使用的foo类型在man typedef foo<...> foo_type
中是硬编码的。我希望调整我的程序,以便支持foo
的集合;类似的东西:
if (desired_foo_str == "2DSimpleFloat")
{
foo<float,Simple,2>(params).method();
}
else if (desired_foo_str == "3DSimpleDouble")
{
foo<double,Simple,3>(params).method();
}
else
{
std::cout << "Unsupported foo."
}
foo的接口不依赖于其模板参数。我的问题是如何改进这个解决方案?我知道boost::mpl
提供了一个类型向量,但与运行时切换相比,它似乎更适合编译时减少。
澄清
让我们说(这是一种简化)我的程序采用N维(由用户提供)中的一组点并集成它们。 SIMD可以加速某些维度,集成方法和标量类型的组合(因此使用模板参数)。 foo<A,B,N>
的所有组合都是有效的,但是不同的用户(所有用户都将编译我的程序)将只需要几个特定的专业来完成他们的工作。我希望允许:
$ integrate --method=2DSimpleFloat mypoints2d.dat
$ integrate --methid=3DSimpleDouble mypoints3d.dat
所以运行时选择他们希望使用的方法。我想知道什么样的框架工作最好能让我将类型与字符串相关联,这样我才能更好地处理上述情况。
答案 0 :(得分:2)
您可以创建模板化的默认方法,该方法会抛出错误,并且您支持的每个组合的模板特化。
class Simple {};
template<typename Scalar, typename Integrator, int Dimension>
class foo
{
public:
void method();
foo() {}
};
// default implementation throws an error
template<typename Scalar, typename Integrator, int Dimension>
void foo<Scalar,Integrator,Dimension>::method() { cout << "unsupported\n"; };
// override default for supported cases:-
template<>
void foo<double,Simple,2>::method() { cout <<"method1\n"; };
template<>
void foo<double,Simple,3>::method() { cout <<"method2\n"; };
// test program
void main() {
foo<float,Simple,2> a; a.method(); // output "unsupported"
foo<double,Simple,2> b; b.method(); // output "method1"
foo<double,Simple,3> c; c.method(); // output "method2"
}
您应该能够在整个班级中自由地混合通用实现和特殊目的; (例如,也许可以使用SIMD内在函数或其他方式处理某些渗透)
如果所有类方法都相同且通用,则限制使用的一种方便方法可能是限制构造函数,以便无法实例化不需要的情况
通常,如果正确使用重载和模板的机制,您应该能够避免在使用它们的地方手动检查类型。 这可以在没有任何指针或虚拟调度的情况下静态链接编译时间。
如果支持的实现是相同的,那么覆盖可以是包装器,以指向另一个模板化方法,如上所述。
答案 1 :(得分:1)
您的问题没有为完整答案提供足够的信息,但我有预感:也许您应该考虑重构代码,以便将独立于参数的部分与依赖于模板的代码分开参数。
典型的例子来自Scott Meyers的书。假设您有一个方矩阵乘数,并将其写为完整模板:
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N>, Matrix<T, N>)
{
// heavy code
}
通过此设置,编译器将为每个大小值N
生成单独的代码片段!这可能是很多代码,N
提供的所有代码都是循环中的绑定。
所以这里的建议是将编译时转换为 runtime 参数并将工作负载重构为单独的函数,并仅使用模板存根来调度调用:< / p>
template <typename T>
void multiply_impl(unsigned int N,
MatrixBuf<T> const & in1, MatrixBuf<T> const & in1,
MatrixBuf<T> & out)
{
// heavy work
}
template <typename T, unsigned int N>
Matrix<T, N> multiply(Matrix<T, N> const & in1, Matrix<T, N> const & in1)
{
Matrix<T, N> out;
multiply_impl(N, in1.buf(), in2.buf(), out.buf());
}
您可以执行类似的操作:将所有与参数无关的代码放在基类中,并创建派生类模板。然后,运行时可以使用工厂函数在运行时创建正确的具体实例。作为继承的替代方法,您还可以创建一个包含私有指针到基类的类型擦除包装类,运行时使用具体的派生实现实例填充它。
答案 2 :(得分:0)
我猜你正在寻找寄存器模式。这只是我的选秀,所以不要依赖它。
class AbstractFooFactory
{
virtual AbstractFoo* create( ParamsType cons& params ) = 0;
// or construct on stack and call .method()
virtual void createAndCallMethod( ParamsType cons& params ) = 0;
};
class FooRegister
{
~FooRegister(); // delete all pointers
template< typename FooFactory >
void operator() ( FooFactory const & factory ) // for boost::mpl:for_each
{ map[factory.getName()]= new FooFactory( factory ); }
AbstractFooFactory* get( std::string name );
std::map< std::string , AbstractFooFactory* > map;
};
template< typename Scalar, typename Integrator, typename Dimension >
class FooFactory: public AbstractFooFactory
{
typedef FooFactory<Scalar, Integrator, Dimension > type; // Metafunction
std::string getName(); // this will be a bit hard to implement
AbstractFoo* create( ParamsType cons& params );
void createAndCallMethod( ParamsType cons& params );
};
简单路径可用于存储类型名称:
template< typename Type >
struct NameTrails
{
static const char const* value;
};
template<> const char const* NameTrails<int>::value = "Int";
template<> const char const* NameTrails<float>::value = "Float";
template<> const char const* NameTrails<double>::value = "Double";
template<> const char const* NameTrails<Simple>::value = "Simple";
template<> const char const* NameTrails<Complex>::value = "Complex";
template< typename Scalar, typename Integrator, typename Dimension >
std::string FooFactory::getName()
{
return boost::lexical_cast<std::string>( Dimension::value ) + "D"
+ NameTrails< Integrator >::value
+ NameTrails< Scalar >::value;
}
现在你需要使用mpl :: for_each注册所有类型:
FooRegister fooRegister;
typedef boost::mpl::vector<Simple,Complex> IntegratorsList;
typedef boost::mpl::vector<int,float,double> ScalarsList;
typedef boost::mpl::range_c<int,1,4> DimensionsList;
typedef boost::mpl::vector<
boost::mpl::vector< Simple, float, boost::mpl::int_<2> >,
boost::mpl::vector< Simple, double, boost::mpl::int_<3> >,
... other types or full cross join ... > FooList;
boost::mpl::for_each< FooList, boost::mpl::quote3<FooFactory> >(
boost::ref(fooRegister) );
我不知道的是如何将IntegratorsList, ScalarList, range_c<int,1,4>
交叉连接到构造完整的FooList。
fooRegister.get("2DSimpleFloat")->createAndCallMethod(params);
你可能想要静态地这样做,所以是的,这是可能的,但我发现很难获得比简单的动态地图或哈希映射更好的性能。