如何处理外部库中的类型冗余?

时间:2009-07-16 14:08:51

标签: design-patterns math type-conversion

我正在编写一个计算机图形应用程序,它使用了几个提供许多所需功能的不同库。

例如,在大学里我编写了四元数,优化了3个向量,并优化了我喜欢使用的4x4矩阵类,因为它们很有效并且我对它们非常熟悉。我还写了一个仅依赖于STL的K-D Tree实现。

最后,我使用ITK来计算给定对称实矩阵的最小特征值(和相关的特征向量)等。

由于所有这些库都包含自己的矩阵和向量实现,我发现自己做了很多这样的事情:

FixedPointType closestPointCoords;
KdTree::HyperPoint target(3);
target[0] = transformedPoint[0];
target[1] = transformedPoint[1];
target[2] = transformedPoint[2];
KdTree::Neighbor result = FixedPointTree.FindNearestNeighbor(target);
minimumDistance = result.GetDistance();
closestPointCoords[0] = result.GetPoint().Get(0);
closestPointCoords[1] = result.GetPoint().Get(1);
closestPointCoords[2] = result.GetPoint().Get(2);

显然我可以编写一个类似于:

的函数
FixedPointType ConvertHyperPointToFixedPointType(const KdTree::HyperPoint &p)
{
    FixedPointType fixedPoint;
    fixedPoint[0] = p[0];
    fixedPoint[1] = p[1];
    fixedPoint[2] = p[2];

    return fixedPoint;
 }

我希望通过这样的方式避免添加库间依赖性:

class HyperPoint
{
    ...
    operator FixedPointType() 
    {
         // Same content as ConvertHyperPointToFixedPointType above
    }
    ...
};

但我想知道是否有人提出了更优雅的解决方案来解决这个问题。

也许我应该从.NET书中获取一个页面并创建一个静态转换类?

您如何处理跨库的类型冗余以及需要经常在各种类型之间进行转换?

此处Adapter模式是否合适?

更新
经过一番探索后,我遇到了TypeConverter。所以这个问题看起来已经在C#中解决了。我仍然在寻找一种干净,高效和简单的方法来用C ++完成它。到目前为止,我正在考虑规范类型,或者只是简单的ConvertHyperPointToFixedPointType类函数。

更新2 经过更多的阅读,听起来像规范类型实际上将充当垫片。有关详细信息,请参阅此paper

以下是一些示例代码,用于说明我目前的倾向:

class VectorConverter
{
public:
    VectorConverter(const VectorImp1 &v1)
    {
        _data[0] = v1[0];
        _data[1] = v1[1];
        _data[2] = v1[2];
    }

    VectorConverter(const VectorImp2 &v2)
    {
        _data[0] = v2.Get(0);
        _data[1] = v2.Get(1);
        _data[2] = v2.Get(2);
    }

    operator VectorImp1()
    {
        return VectorImp1(_data[0], _data[1], _data[2]);
    }

    operator VectorImp2()
    {
        VectorImp2 v2;
        v2.Set(0, _data[0]);
        v2.Set(1, _data[1]);
        v2.Set(2, _data[2]);
        return v2;
    }

    // actual vector implementation goes here or a wrapper around an existing one
};

// example usage
VectorImp1 closestPointCoords, target;
// for the sake of illustration I included the cast, but you could implicitly cast
KdTree::Neighbor result = FixedPointTree.FindNearestNeighbor((VectorImp2)VectorConverter(target));
minimumDistance = result.GetDistance();
closestPointCoords = (VectorImp1)VectorConverter(result.GetPoint());

感谢您的帮助。如果有人遇到更好的事情,请随时更新这篇文章。

2 个答案:

答案 0 :(得分:1)

CAVEAT:我现在面前没有编译器,我突然不确定SFINAE部分(不确定它是否适用于功能体,或仅适用于参数类型)。如果错了,请有人纠正。

在C ++中,您可以利用不同类的类似语法来创建单个函数模板:

template <class ToType, class FromType>
ToType ConvertPoint(const FromType& FromValue)
{
  ToType ToValue;
  ToValue[0] = FromValue[0];
  ToValue[1] = FromValue[1];
  ToValue[2] = FromValue[2];

  return ToValue;
}

// If the version above failed, the compiler will try this one.
// This is called SFINAE
template <class ToType, class FromType>
ToType ConvertPoint(const FromType& FromValue)
{
  ToType ToValue;
  ToValue[0] = FromValue.Get(0);
  ToValue[1] = FromValue.Get(1);
  ToValue[2] = FromValue.Get(2);

  return ToValue;
}

// Usage:
KdTree::HyperPoint hyperPoint;
FixedPointType fixedPoint = ConvertPoint<FixedPointType>(hyperPoint);

我想你可以在C#中做同样的事情,虽然我对语法不太熟悉。

对于您的类不完全遵循相同语法约定的情况,SFINAE通过让您提供多个变体来帮助您 - 如果一个失败,编译器将选择下一个并尝试使用它。使用shims,您可以通过提供一个通用函数来限制变体的数量,该函数使用适当定义的填充来设置和获取所有现有类的每个坐标。

答案 1 :(得分:1)

我认为Carl的解决方案是C ++中最简单的。 适配器模式的问题在于,适配类型应该是接口或抽象类,或者是具有多个虚方法的类。 通常,矩阵没有这些条件,因为矩阵是值类型,在它上面实现接口并不自然。 (我的意思是值类型是等式不是由对象的身份定义,而是由其字段定义)

如果您真的想这样做,可以创建一个IMatrix接口并为每个适配器类型实现接口。

另一个解决方案是创建canonical类型,并为此类型的Matrix版本创建转换器。

这两种方式在代码行中是等效的,但第二种方式更直观。