代码之家  ›  专栏  ›  技术社区  ›  Gabe

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

  •  1
  • Gabe  · 技术社区  · 15 年前

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

    例如,在大学里,我写了四元数,优化了3个向量,优化了4x4矩阵类,我喜欢使用它们,因为它们是有效的,我非常熟悉它们。我还编写了一个只依赖STL的k-d树实现。

    最后,我使用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 回复  |  直到 15 年前
        1
  •  1
  •   Nicolas Dorier    15 年前

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

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

    另一个解决方案是创建 canonical 为每个版本的矩阵键入并创建此类型的转换器。

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

        2
  •  1
  •   Carl Seleborg    15 年前

    警告: 现在我面前没有编译器,我突然对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 ,您可以通过提供一个通用函数来限制变量的数量,该函数使用适当定义的垫片来设置和获取所有现有类的每个坐标。