调用带有非常量指针参数const_cast,reinterpret_cast,洗钱的c函数的正确方法

时间:2018-11-13 00:05:01

标签: c c++17 reinterpret-cast const-cast

调用从C ++获取非常量自定义指针参数的C函数的正确方法是什么?

作为一个非常常见的示例,请使用FFTW3中的函数fftw_plan_dft_1dhttp://fftw.org/fftw3_doc/Complex-DFTs.html#Complex-DFTs

fftw_plan fftw_plan_dft_1d(int n0,
                           fftw_complex *in, fftw_complex *out,
                           int sign, unsigned flags);

({fftw_complexdouble[2]的typedef)。

假设我想将此功能应用于几个const正确的c ++容器。

std::vector<std::complex<double>> const In = {...};
std::vector<std::complex<double>> Out(In.size());

我该怎么做?

_第一次迭代,我必须从容器中提取数据指针,

assert(In.size() == Out.size());
fftw_plan fftw_plan_dft_1d(In.size(),
                           In.data(), Out.data(), // error const
                           FFTW_FORWARD, FFTW_ESTIMATE);

_第二次迭代

但是由于它是const,所以我必须constcast。 我假设这是唯一可行的解​​决方案,并假设C交互的原因是C没有const参数。

fftw_plan p = fftw_plan_dft_1d(In.size(),
                           const_cast<std::complex<double>*>(In.data()), // error std::complex is not convertible to fftw_complex
                           Out.data(), 
                           FFTW_FORWARD, FFTW_ESTIMATE);

_第三次迭代

现在,我必须将std::complex<double>转换为fftw_complexdouble[2])。幸运的是,std::complex<double>必须具有与double[2]相同的布局。

fftw_plan p = fftw_plan_dft_1d(In.size(),
                           reinterpret_cast<fftw_complex*>(const_cast<std::complex<double>*>(In.data())), // is this UB?
                           reinterpret_cast<fftw_complex*>(Out.data()), 
                           FFTW_FORWARD, FFTW_ESTIMATE);

现在我很偏执,显然reinterpret_cast一直是UB。 我不知道如何使用std::launder,但我知道在某些情况下它可以节省reinterpret_cast UB。

fftw_plan p = fftw_plan_dft_1d(In.size(),
                           std::launder(reinterpret_cast<fftw_complex*>(const_cast<std::complex<double>*>(In.data()))), // needs c++17
                           std::launder(reinterpret_cast<fftw_complex*>(Out.data())), 
                           FFTW_FORWARD, FFTW_ESTIMATE);

最终,这是调用包含const和类型重新解释的C函数的一种合理方法吗?

我也太偏执吗?还是只是在这种情况下从C ++调用C始终是正式的UB,而我对此却无能为力?

1 个答案:

答案 0 :(得分:4)

我认为您确实很偏执,我也认为这是一件好事。保持。有点妄想症会大大减少您用脚射击自己的次数!

您正确地确定了需要丢弃const限定符的原因,因为该库在其函数签名中未使用const。然后您使用const_cast<>正确地确定了解决方案。

在这种情况下,您还正确地识别出reinterpret_cast<>在技术上是 UB 如果 ,您不认为{{1 }}的类型定义为fftw_complex。 (我不熟悉FFTW3,所以我不知道这是否正确,但是您可能确实知道。)如果您知道它是一个typedef,则它不是UB,因为类型相同,只是在下面加上别名不同的名字。如果您不知道,它“可能” 还是安全的,但是,是的,我认为在这种情况下,您必须进行一点飞跃,因为知道任何理智的,真实的编译器应该做正确的事。 FFTW3文档中有一个note to this effect

  

C ++具有其自己的复杂模板类,该类在标准头文件中定义。据报道,C ++标准委员会最近同意强制要求用于此类型的存储格式与C99类型二进制兼容,即具有连续实数[0]和虚数[1]的数组T [2]。 (请参阅报告http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2002/n1388.pdf WG21 / N1388。)尽管在撰写本文时还不是正式标准的一部分,但该提案指出:“此解决方案已在标准库的所有当前主要实现中进行了测试,并显示出工作的效用。在一定程度上,如果您有一个复杂的变量* x,则可以通过reinterpret_cast(x)将其直接传递给FFTW。

(当然,this layout guarantee 现在已成为C ++ 11中标准的一部分。)

最后,有关C ++样式转换的说明。每个人都说您应该使用它们而不是C强制转换,这在大多数情况下都是正确的,但是C风格的强制转换定义明确的,程序不会如果使用它们会炸毁。权衡是简洁和可读的代码(C风格)之一,反对意图的显式声明(C ++风格)。 C ++编译器使用C样式强制转换are defined here的确切规则。我个人认为,由于您已经在使用功能标记不够完善的C库,所以简单地将C样式强制转换为double[2]并称之为一天并不是世界末日