处理非 const-aware Library/SDK 的最佳方法是什么?
What's the best way to deal with a non-const-aware Library/SDK?
我现在使用 3ds max SDK 的时间更长,几乎所有部分都根本不使用 const
。因此,即使 Bitmap
的 Width()
或 Height()
getter 也不会被标记为 const
。这在小项目中已经是一个真正的烦恼,但是自从我一直在做一个更大的项目,它变得越来越糟糕。
例如,出于性能原因,我持有一个 Bitmap
实例作为多个 class 实例中的 shared_ptr<Bitmap>
成员。当然,在某些情况下我想尽一切可能避免单个实例可能会更改所有实例的属性,因此所有原始指针 getters(SDK 所必需的)都会提供 const Bitmap*
。不幸的是,现在我什至不能问 const Bitmap*
它的宽度 - 因为 Width()
是非常量。
我在问自己处理这个问题的最佳方法是什么。我看到三个选项:
- 完全忘记 const,让一切都成为非 const。在我以前做的较小的项目中,但正如我所说,使用更复杂的技术,它变得更危险。
- 在每个需要的地方制作一个就地
const_cast
。很多地方都会这样,读起来很不爽。
- 为 3ds max classes 编写包装器,它至少为可能非常安全的方法提供
const
方法。这会将所有 const_cast
封装在一个地方,也适用于其他项目。
我被警告过(而且我知道)这可能是基于意见的。但是我不得不处理这个恼人的问题很长时间了,我真的很想找到解决办法,因此需要其他人的经验。
首先,我想提一下 const
正确性的缺乏可能在实现细节上是合理的,例如 getter 函数可能对内部同步原语执行锁定,因此总是改变内部状态,不能标记为 const
:
int Bitmap::Width(void)
{
int width{};
::std::lock_guard<::std::mutex> const lock{m_sync};
width = m_width;
return width;
}
作为解决方法,您可以编写一个专用的 PImpl 位图包装器,使用适当的 const
限定符限制直接访问感兴趣的位图实现转发函数:
class SharedBitmap
{
private: ::std::shared_ptr<Bitmap> m_p_bitmap;
public: int Width(void) const
{
return m_p_bitmap->Width();
}
// other methods...
};
请注意,此方法不同于所列的第三个选项,因为它不涉及 const_cast
。
根据我的经验(10 年),"const" 带来的麻烦多于帮助。更不用说代码越来越长,因此更难阅读。如果你想知道一个库是如何工作的,你无论如何都要阅读手册,而不是 header。如果你想知道你做对了,你 运行 功能测试。该死的,甚至还有静态分析工具检查变量是否曾被写入,而不会为代码增加无用的 non-functional 元数据来捕获未记录的使用模式。而且由于有很多方法可以破坏 const,因此它是捕获此类错误的正确方法。
总而言之,根据我的经验,选项 1 是最有效的解决方案。 (这是一个意见吗?不同意的人可能会这么认为。)
对于快速 post-purge 的 const,您可以 #define const
甚至 -Dconst
删除它,尽管 它是否安全可能取决于您的具体情况,一个 illegal use 正在为标准 header 执行此操作。我做过类似的 hack,比如 #define private|protected public
而不是在进行白盒测试时弄乱 friend
,效果很好!
要知道 "constant variable" 的概念在许多编程语言中都是无效的,没有它它们似乎也能正常工作。
唯一需要 const 的情况是 C-string 常量/字符串文字 。不过你的情况似乎不是这样。
我现在使用 3ds max SDK 的时间更长,几乎所有部分都根本不使用 const
。因此,即使 Bitmap
的 Width()
或 Height()
getter 也不会被标记为 const
。这在小项目中已经是一个真正的烦恼,但是自从我一直在做一个更大的项目,它变得越来越糟糕。
例如,出于性能原因,我持有一个 Bitmap
实例作为多个 class 实例中的 shared_ptr<Bitmap>
成员。当然,在某些情况下我想尽一切可能避免单个实例可能会更改所有实例的属性,因此所有原始指针 getters(SDK 所必需的)都会提供 const Bitmap*
。不幸的是,现在我什至不能问 const Bitmap*
它的宽度 - 因为 Width()
是非常量。
我在问自己处理这个问题的最佳方法是什么。我看到三个选项:
- 完全忘记 const,让一切都成为非 const。在我以前做的较小的项目中,但正如我所说,使用更复杂的技术,它变得更危险。
- 在每个需要的地方制作一个就地
const_cast
。很多地方都会这样,读起来很不爽。 - 为 3ds max classes 编写包装器,它至少为可能非常安全的方法提供
const
方法。这会将所有const_cast
封装在一个地方,也适用于其他项目。
我被警告过(而且我知道)这可能是基于意见的。但是我不得不处理这个恼人的问题很长时间了,我真的很想找到解决办法,因此需要其他人的经验。
首先,我想提一下 const
正确性的缺乏可能在实现细节上是合理的,例如 getter 函数可能对内部同步原语执行锁定,因此总是改变内部状态,不能标记为 const
:
int Bitmap::Width(void)
{
int width{};
::std::lock_guard<::std::mutex> const lock{m_sync};
width = m_width;
return width;
}
作为解决方法,您可以编写一个专用的 PImpl 位图包装器,使用适当的 const
限定符限制直接访问感兴趣的位图实现转发函数:
class SharedBitmap
{
private: ::std::shared_ptr<Bitmap> m_p_bitmap;
public: int Width(void) const
{
return m_p_bitmap->Width();
}
// other methods...
};
请注意,此方法不同于所列的第三个选项,因为它不涉及 const_cast
。
根据我的经验(10 年),"const" 带来的麻烦多于帮助。更不用说代码越来越长,因此更难阅读。如果你想知道一个库是如何工作的,你无论如何都要阅读手册,而不是 header。如果你想知道你做对了,你 运行 功能测试。该死的,甚至还有静态分析工具检查变量是否曾被写入,而不会为代码增加无用的 non-functional 元数据来捕获未记录的使用模式。而且由于有很多方法可以破坏 const,因此它是捕获此类错误的正确方法。
总而言之,根据我的经验,选项 1 是最有效的解决方案。 (这是一个意见吗?不同意的人可能会这么认为。)
对于快速 post-purge 的 const,您可以 #define const
甚至 -Dconst
删除它,尽管 它是否安全可能取决于您的具体情况,一个 illegal use 正在为标准 header 执行此操作。我做过类似的 hack,比如 #define private|protected public
而不是在进行白盒测试时弄乱 friend
,效果很好!
要知道 "constant variable" 的概念在许多编程语言中都是无效的,没有它它们似乎也能正常工作。
唯一需要 const 的情况是 C-string 常量/字符串文字 。不过你的情况似乎不是这样。