c - 调用采用非常量指针参数、const_cast、reinterpret_cast、launder 的 c 函数的正确方法

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

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

以函数 fftw_plan_dft_1d 作为一个非常常见的示例来自 FFTW3。 http://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] 的类型定义)。

假设我想将此函数应用于几个常量正确的 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 接口(interface)的原因是 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_complex (double[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 而我对此无能为力?

最佳答案

我认为你确实很偏执,而且我也认为这是一件好事。继续努力吧。一点偏执就会大大减少搬起石头砸自己脚的次数!

您正确地确定了放弃 const 的需要限定符,因为该库不使用 const在其函数签名中。并且您使用 const_cast<> 正确识别了解决方案.

您还正确地识别出reinterpret_cast<> 技术上在这种情况下是UB如果您不认为fftw_complex类型定义为 double[2] 。 (我不熟悉 FFTW3,所以我不知道这是否正确,但你可能知道。)如果你知道它是一个 typedef,它不是 UB,因为类型是相同的,只是在下面使用别名不同的名字。如果你不知道,它“可能”仍然是安全的,但是,是的,我认为这可能是你必须做出一点信仰飞跃的情况,知道任何理智的、现实世界的编译器应该做正确的事。有一个note to this effect在 FFTW3 文档中。

C++ has its own complex template class, defined in the standard header file. Reportedly, the C++ standards committee has recently agreed to mandate that the storage format used for this type be binary-compatible with the C99 type, i.e. an array T[2] with consecutive real [0] and imaginary [1] parts. (See report http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2002/n1388.pdf WG21/N1388.) Although not part of the official standard as of this writing, the proposal stated that: “This solution has been tested with all current major implementations of the standard library and shown to be working.” To the extent that this is true, if you have a variable complex *x, you can pass it directly to FFTW via reinterpret_cast(x).

(当然 this layout guarantee 现在是 C++11 标准的一部分。)

最后,关于 C++ 风格转换的说明。每个人都说你应该使用它们而不是 C 类型转换,在大多数情况下都是如此,但是 C 风格类型转换定义良好并且程序不会如果你使用它们就会爆炸。权衡之一是简洁和可读的代码(C 风格)与显式意图声明(C++ 风格)。 C++ 编译器使用 C 样式强制转换执行操作的确切规则 are defined here 。在我个人看来,由于您已经在处理函数签名不太完美的 C 库,因此简单地以 C 风格强制转换为 (double *) 并不是世界末日。到此为止。

关于c - 调用采用非常量指针参数、const_cast、reinterpret_cast、launder 的 c 函数的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53271883/

相关文章:

c - 在内存中打开和加载文件的方法

c - 在 Pic32 中启用 DHCP

c++ - 枚举折叠表达式

c++ - 将 STL complex<double> 转换为 fftw_complex 的问题

c - 获取环回地址而不是实际地址?

c - 在 fread/fwrite 期间去除 AES 填充

C++11、C++14、C++17?用什么?

c++ - C++ 17,初始化数组

c++ - 将对类的引用转发为另一种类型