在不违反严格的别名规则的情况下调用 FFTW 的就地实数到复数转换
Calling FFTW's in-place real-to-complex transform without violating strict aliasing rules
我想调用 fftw's in-place real-to-complex transform 函数,它具有以下签名:
fftw_plan fftw_plan_dft_r2c_1d(
int n, // transform length
double* in, // pointer to input array
fftw_complex* out, // pointer to output array
unsigned flags // flags
);
文档说我应该表明我希望通过为 in
和 out
参数传递别名指针来执行就地转换。
问题: in
和 out
如何在不违反严格的别名规则的情况下使用别名?
我对 GCC 特定的扩展持开放态度(即,使用 union
s 进行类型双关,即使标准声明这是未定义的行为)。即使允许此扩展,联合也不能包含动态大小的数组(这在这个应用程序中是必须的——我事先不知道转换长度)。有人有什么想法吗?提前致谢。
我会挑战这个前提:不要太担心严格的别名。
创建一个 double
的数组并将指向它的指针传递给 in
。 reinterpret_cast
指向 fftw_complex *
的指针并将其传递给 out
.
从此数组中读取结果 double
s(作为复数的实部和虚部对)。
是的,如果以这种方式调用,fftw_plan_dft_r2c_1d
可能会打破幕后的严格别名。
但是由于它在一个单独的翻译单元中,并且调用者没有违反严格别名,您的编译器无法判断是否确实违反了严格别名。
fftw_complex
是 essentially 一个 struct fftw_complex {double re, im;};
,所以一切正常。
为了更加安全,您可以添加:
static_assert(sizeof(fftw_complex) == 2 * sizeof(double) && alignof(fftw_complex) <= alignof(double));
根据this linkfftw_complex
是下面的typedef
:
typedef double fftw_complex[2];
并且根据 C++20 之前的规则 fftw_complex*
可能会因为这个别名 double*
([basic.lval]p8.6):
If a program attempts to access the stored value of an object through a glvalue of other than one of the
following types the behavior is undefined:
...
— an aggregate or union type that includes one of the aforementioned
types among its elements or nonstatic data members (including,
recursively, an element or non-static data member of a subaggregate or
contained union)
数组是一个集合,我们的数组包含 double
s 因此它允许别名 double
指针。因此,fftw_plan_dft_r2c_1d
函数中没有违反严格的别名规则,您可以安全地使用它。
但是请注意,此段已从 C++20 标准中删除,并且正在争论是否也应将其从 C 标准中删除。但是因为它还没有被删除并且 GCC & clang 实际上尊重它我想可以安全地假设行为不会随着 C++20 实现而改变。据我所知,MSVC 根本没有利用 SAR。
此构造并不一定意味着违反别名。
在 fftw_plan_dft_r2c_1d
中,可能有一个 placement new array 调用,它正确地创建了 out
缓冲区。
对于 C++17,您可能希望在调用后 std::launder
out
指针,以绝对符合标准。
我想调用 fftw's in-place real-to-complex transform 函数,它具有以下签名:
fftw_plan fftw_plan_dft_r2c_1d(
int n, // transform length
double* in, // pointer to input array
fftw_complex* out, // pointer to output array
unsigned flags // flags
);
文档说我应该表明我希望通过为 in
和 out
参数传递别名指针来执行就地转换。
问题: in
和 out
如何在不违反严格的别名规则的情况下使用别名?
我对 GCC 特定的扩展持开放态度(即,使用 union
s 进行类型双关,即使标准声明这是未定义的行为)。即使允许此扩展,联合也不能包含动态大小的数组(这在这个应用程序中是必须的——我事先不知道转换长度)。有人有什么想法吗?提前致谢。
我会挑战这个前提:不要太担心严格的别名。
创建一个 double
的数组并将指向它的指针传递给 in
。 reinterpret_cast
指向 fftw_complex *
的指针并将其传递给 out
.
从此数组中读取结果 double
s(作为复数的实部和虚部对)。
是的,如果以这种方式调用,fftw_plan_dft_r2c_1d
可能会打破幕后的严格别名。
但是由于它在一个单独的翻译单元中,并且调用者没有违反严格别名,您的编译器无法判断是否确实违反了严格别名。
fftw_complex
是 essentially 一个 struct fftw_complex {double re, im;};
,所以一切正常。
为了更加安全,您可以添加:
static_assert(sizeof(fftw_complex) == 2 * sizeof(double) && alignof(fftw_complex) <= alignof(double));
根据this linkfftw_complex
是下面的typedef
:
typedef double fftw_complex[2];
并且根据 C++20 之前的规则 fftw_complex*
可能会因为这个别名 double*
([basic.lval]p8.6):
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
...
— an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union)
数组是一个集合,我们的数组包含 double
s 因此它允许别名 double
指针。因此,fftw_plan_dft_r2c_1d
函数中没有违反严格的别名规则,您可以安全地使用它。
但是请注意,此段已从 C++20 标准中删除,并且正在争论是否也应将其从 C 标准中删除。但是因为它还没有被删除并且 GCC & clang 实际上尊重它我想可以安全地假设行为不会随着 C++20 实现而改变。据我所知,MSVC 根本没有利用 SAR。
此构造并不一定意味着违反别名。
在 fftw_plan_dft_r2c_1d
中,可能有一个 placement new array 调用,它正确地创建了 out
缓冲区。
对于 C++17,您可能希望在调用后 std::launder
out
指针,以绝对符合标准。