Можно ли безопасно переопределить std :: vector & lt; std :: complex & lt; boost: multiprecision :: float128 & gt; & gt; (N) .data () в fftwq_complex *?

Я не ожидал, что следующий пример будет работать, но на самом деле он работает (g ++ 4.6.4, с —std = c ++ 0x):

#include <boost/multiprecision/float128.hpp>
#include <blitz/array.h>
#include <fftw3.h>int main(int /*argc*/, char** /*argv*/)
{
//these are the same
std::cout << sizeof(std::complex<boost::multiprecision::float128>) << " " << sizeof(fftwq_complex) << std::endl;typedef std::vector< std::complex<boost::multiprecision::float128> >   boost128cvec;
//typedef std::vector<std::complex<boost::multiprecision::float128> , fftw::allocator< std::complex<boost::multiprecision::float128> > >   boost128cvec;

//declare a std::vector consisting of std::complex<boost::multiprecision::float128>
boost128cvec test_vector3(12);

//casting its data storatge to fftwq_complex*
fftwq_complex* test_ptr3 = reinterpret_cast<fftwq_complex*>(test_vector3.data());

//also create a view to the same data as a blitz::Array
blitz::Array<std::complex<boost::multiprecision::float128>, 1> test_array3(test_vector3.data(), blitz::TinyVector<int, 1>(12), blitz::neverDeleteData);

test_vector3[3] = std::complex<boost::multiprecision::float128>(1.23,4.56);

//this line would not work with std::vector
test_array3 = sin(test_array3);

//this line would not work with the built-in type __float128
test_vector3[4] = sin(test_vector3[3]);

//all of those print the same numbers
std::cout << "fftw::vector: " << test_vector3[3].real()       << " + i " << test_vector3[3].imag() << std::endl;
std::cout << "fftw_complex: " << (long double)test_ptr3[3][0] << " + i " << (long double)test_ptr3[3][1] << std::endl;
std::cout << "blitz:        " << test_array3(3).real()        << " + i " << test_array3(3).imag() << std::endl << std::endl;

}

Два замечания:

  • Цель состоит в том, чтобы иметь возможность использовать оба fftw а также blitz::Array операции с одними и теми же данными без необходимости их копирования, и в то же время возможность использования общих функций, таких как sin() также для сложных переменных с четверной точностью
  • blitz-часть работает отлично, что и ожидается. Но сюрпризом (для меня) было то, что fftwq_complex* часть тоже работает отлично.
  • fftw::allocator это простая замена std::allocator который будет использовать fftwq_malloc чтобы обеспечить правильное выравнивание simd, но это не важно для этого вопроса, поэтому я оставил его (по крайней мере, я думаю, что это не важно для этого вопроса)

Мой вопрос: на какой тонкий лед я наступаю?

0

Решение

Вы в значительной степени экономите

  • std::vector совместим с массивом C (вы можете получить доступ к указателю на первый элемент через vector.data(), как ответили в этот вопрос
  • std::complex<T> разработан для совместимости с массивом форм T[2], который совместим с FFTW. Это описано в Документация FFTW

    C ++ имеет свой собственный сложный шаблонный класс, определенный в стандартном заголовочном файле. Как сообщается, комитет по стандартам C ++ недавно принял решение о том, чтобы формат хранения, используемый для этого типа, был двоично-совместимым с типом C99, то есть массивом T [2] с последовательными действительными [0] и мнимыми [1] частями. (См. Отчет http://www.open-std.org/jtc1/sc22/WG21/docs/papers/2002/n1388.pdf WG21 / N1388.) Хотя это и не является частью официального стандарта на момент написания этой статьи, в предложении указывалось, что: «Это решение было протестировано со всеми основными реализациями стандартной библиотеки и показано, что оно работает». В той степени, в которой это true, если у вас есть переменная complex * x, вы можете передать ее непосредственно в FFTW через reinterpret_cast (x).

Единственное, что нужно иметь в виду, это то, что data() становится недействительным, если вы добавите значения в ваш вектор.


Для последней части есть совместимость между boost::multiprecision::float128 а также __float128, Дополнительная документация не дает никаких гарантий по этому поводу.
Однако, что можно сделать, это добавить несколько статических утверждений в ваш код, что приводит к сбою, если преобразование невозможно. Это может выглядеть так:

static_assert(std::is_standard_layout<float128>::value,"no standard type");
static_assert(sizeof(float128) == sizeof(__float128),"size mismatch");

куда sizeof гарантирует одинаковый размер типа наддува и __float128, и is_standard_layout проверяет, что:

Указатель на класс стандартного макета может быть преобразован (с помощью reinterpret_cast) в указатель на его первый нестатический элемент данных и наоборот.

Конечно, это дает только подсказки, если оно работает в конце, так как вы не можете сказать, является ли тип действительно __float128, но ab boost заявляет, что их тип — это тонкая обертка вокруг него, все должно быть хорошо. Если они являются изменениями в дизайне или структуре float128статические утверждения должны потерпеть неудачу.

1

Другие решения


По вопросам рекламы [email protected]