【发布时间】:2016-02-12 11:00:13
【问题描述】:
我在我的代码中使用这种类型的模式来处理各种事物的特征。首先,我有一组特征模板;这些由枚举值专门化:
template<int>
struct PixelProperties;
/// Properties of UINT8 pixels.
template<>
struct PixelProperties< ::ome::xml::model::enums::PixelType::UINT8> :
public PixelPropertiesBase<PixelProperties< ::ome::xml::model::enums::PixelType::UINT8> >
{
/// Pixel type (standard language type).
typedef uint8_t std_type;
/// Pixel type (big endian).
typedef boost::endian::big_uint8_t big_type;
/// Pixel type (little endian).
typedef boost::endian::little_uint8_t little_type;
/// Pixel type (native endian).
typedef boost::endian::native_uint8_t native_type;
/// This pixel type is not signed.
static const bool is_signed = false;
/// This pixel type is integer.
static const bool is_integer = true;
/// This pixel type is not complex.
static const bool is_complex = false;
};
然后我就有了使用这些特征的代码。大多数情况下,它直接使用它们,但在某些情况下它需要打开枚举值,例如:
bool
isComplex(::ome::xml::model::enums::PixelType pixeltype)
{
bool is_complex = false;
switch(pixeltype)
{
case ::ome::xml::model::enums::PixelType::INT8:
is_complex = PixelProperties< ::ome::xml::model::enums::PixelType::INT8>::is_complex;
break;
case ::ome::xml::model::enums::PixelType::INT16:
is_complex = PixelProperties< ::ome::xml::model::enums::PixelType::INT16>::is_complex;
break;
[...]
}
return is_complex;
}
这是用于运行时而非编译时的自省。我的问题是它需要在 switch 语句中对每个枚举进行大小写,这很难维护。我现在有一种情况,我需要处理两组枚举的所有组合,如果我要像上面那样处理它,这将需要嵌套的 switch 语句。组合复杂性显然不会扩展,但与此同时,除了显式之外,我看不到驱动每个组合的模板扩展的好方法。这是使用这种组合扩展进行运行时单位转换的人为示例:
#include <iostream>
#include <boost/units/unit.hpp>
#include <boost/units/make_scaled_unit.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/systems/si.hpp>
using boost::units::quantity;
using boost::units::quantity_cast;
using boost::units::make_scaled_unit;
using boost::units::scale;
using boost::units::static_rational;
namespace si = boost::units::si;
enum LengthUnit
{
MILLIMETRE,
MICROMETRE,
NANOMETRE
};
template<int>
struct UnitProperties;
template<>
struct UnitProperties<MILLIMETRE>
{
typedef make_scaled_unit<si::length,scale<10,static_rational< -3> > >::type unit_type;
};
template<>
struct UnitProperties<MICROMETRE>
{
typedef make_scaled_unit<si::length,scale<10,static_rational< -6> > >::type unit_type;
};
template<>
struct UnitProperties<NANOMETRE>
{
typedef make_scaled_unit<si::length,scale<10,static_rational< -9> > >::type unit_type;
};
struct Quantity
{
double value;
LengthUnit unit;
};
template<int SrcUnit, int DestUnit>
double
convert(double value)
{
typedef typename UnitProperties<SrcUnit>::unit_type src_unit_type;
typedef typename UnitProperties<DestUnit>::unit_type dest_unit_type;
quantity<src_unit_type, double> src(quantity<src_unit_type, double>::from_value(value));
quantity<dest_unit_type, double> dest(src);
return quantity_cast<double>(dest);
}
Quantity
convert(Quantity q, LengthUnit newunit)
{
switch(q.unit)
{
case MILLIMETRE:
switch(newunit)
{
case MILLIMETRE:
return Quantity({convert<MILLIMETRE, MILLIMETRE>(q.value), MILLIMETRE});
break;
case MICROMETRE:
return Quantity({convert<MILLIMETRE, MICROMETRE>(q.value), MICROMETRE});
break;
case NANOMETRE:
return Quantity({convert<MILLIMETRE, NANOMETRE>(q.value), NANOMETRE});
break;
}
break;
case MICROMETRE:
switch(newunit)
{
case MILLIMETRE:
return Quantity({convert<MICROMETRE, MILLIMETRE>(q.value), MILLIMETRE});
break;
case MICROMETRE:
return Quantity({convert<MICROMETRE, MICROMETRE>(q.value), MICROMETRE});
break;
case NANOMETRE:
return Quantity({convert<MICROMETRE, NANOMETRE>(q.value), NANOMETRE});
break;
}
break;
case NANOMETRE:
switch(newunit)
{
case MILLIMETRE:
return Quantity({convert<NANOMETRE, MILLIMETRE>(q.value), MILLIMETRE});
break;
case MICROMETRE:
return Quantity({convert<NANOMETRE, MICROMETRE>(q.value), MICROMETRE});
break;
case NANOMETRE:
return Quantity({convert<NANOMETRE, NANOMETRE>(q.value), NANOMETRE});
break;
}
break;
}
}
int main()
{
Quantity q { 34.5, MICROMETRE };
auto r = convert(q, NANOMETRE);
std::cout << q.value << " micrometres is " << r.value << " nanometres\n";
}
在其他情况下,我使用 boost::variant 及其 static_visitor 来推动所有组合的扩展。这很好用,但在这里可能行不通——不同特征的类型可能相同,但行为不同。除非可以对变量类型中的枚举值进行编码?
或者 Boost 预处理器宏或 C++11 可变参数模板能够提供更好的解决方案吗?编辑:或者可能是 boost::mpl::foreach?
感谢您的任何建议, 罗杰
【问题讨论】:
-
你能发一个minimal reproducible example 来展示“两组枚举的组合”吗?
-
当然可以。我添加了一个简单的示例,以使用具有 3×3 枚举(9 种组合)的嵌套 switch 语句来演示这一点。正是这种情况下,我有兴趣改进可维护性和运行时效率。 (在实践中,我将至少使用 20 种组合,制作约 400 种组合)。