【问题标题】:How to handle type redundancy in external libraries?如何处理外部库中的类型冗余?
【发布时间】:2009-07-16 14:08:50
【问题描述】:

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

例如,在大学时,我编写了四元数、优化的 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 书籍中取出一页并创建一个静态 Convert 类?

您如何处理跨库的类型冗余以及在各种类型之间频繁转换的需求?

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());

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

【问题讨论】:

    标签: design-patterns math type-conversion


    【解决方案1】:

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

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

    另一种解决方案是创建canonical 类型,并为每个版本的Matrix 创建此类型的翻译器。

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

    【讨论】:

    • 我假设您所说的“规范类型”是指只选择一个矩阵/向量实现并始终将所有内容转换为该类型/从该类型转换。所以在上面的例子中,我会将transformedPoint 转换为某种CanonicalVector,然后在将其传递给KdTree 之前将其转换为HyperPoint。所以 CanonicalVector 将实现 FromFixedPointType() 和 ToHyperPoint()。听起来对吗?
    • 是的,这就是我的意思,没有规范类型和 n 类型你有 n!翻译成代码。使用规范,您只有 2n 种代码翻译。
    • 我犯了一个错误,没有规范类型有 f(n) = f(n-1) + 2(n-1) 转换为代码,但这并没有改变我的观点.
    【解决方案2】:

    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,您可以通过提供一个通用函数来限制变体的数量,该函数使用适当定义的垫片来设置和获取所有现有类的每个坐标。

    【讨论】:

    • 感谢 shim 链接,我以前没有听说过这个词。我认为这是朝着正确方向迈出的一步。我宁愿回避模板化函数,因为这些类的接口不一定是标准的(例如,我不能假设 [] 运算符已定义)。不过,其中一些术语使我想到了这一点:en.wikipedia.org/wiki/Adapter_pattern 我想我可以在这里应用这种模式。
    • 哇,SFINAE 看起来很强大。这是标准的 C++ 功能吗?如果 gcc 和 microsoft 处理相同,那么这可能是处理此问题的最佳方法。
    • visual studio 不喜欢它:错误 1 ​​错误 C2995: 'ToType ConvertPoint(const FromType &)' : 函数模板已被定义 wikipedia 示例仅显示此与参数一起使用。
    猜你喜欢
    • 1970-01-01
    • 2014-02-20
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 2017-09-26
    相关资源
    最近更新 更多