Я использую стороннюю библиотеку C ++, чтобы сделать тяжелую работу в Джулии. Со стороны Юлии данные хранятся в объекте типа Array{Float64, 2}
(это примерно похоже на массив двойных чисел). Я могу передать это в C ++, используя указатель на double
, Однако на стороне C ++ данные хранятся в структуре, называемой vector3
:
typedef struct _vector3
{
double x, y, z;
} vector3;
Мой быстрый и грязный подход состоит из пяти этапов:
double*
в vector3*
vector3*
в double*
Копирование больших объемов данных очень неэффективно. Есть ли какой-то тайный трюк, который я могу использовать, чтобы избежать копирования данных из double
в struct
и назад? Я хочу как-то интерпретировать одномерный массив double
(с размером, кратным 3) как одномерный массив структуры с 3 double
члены.
Строго говоря, вы не можете. Я задавал подобный вопрос несколько раз назад (Псевдоним структуры и массива способом C ++), и ответы объяснили, почему прямое наложение имен вызвало неопределенное поведение, и дали некоторые подсказки о возможных обходных путях.
Тем не менее, вы уже находитесь в угловом деле, потому что исходные данные приходят с другого языка. Это означает, что обработка этих данных не охватывается стандартом C ++ и определяется только реализацией, которую вы используете (gcc / version или clang / version или …)
Для вашей реализации может быть допустимым псевдоним внешнего массива для структуры C ++ или внешней структуры для массива C ++. Вы должны тщательно документировать документацию по смешанному языку для вашей точной реализации.
К сожалению, нет, вы не можете. И это из-за правило алиасинга что C ++ имеет. Короче если у вас есть объект T
вы не можете легально получить к нему доступ из указателя несовместимого типа U
, В этом смысле вы не можете получить доступ к объекту типа double
или же double*
через указатель типа struct _vector3
или наоборот.
Если вы копаете достаточно глубоко, вы найдете reinterpret_cast
и, возможно, подумайте «О, это именно то, что мне нужно», но это не так. Неважно, какую хитрость ты делаешь (reinterpret_cast
или иным образом), чтобы обойти языковые ограничения (также известные как просто скомпилируйте) факт остается фактом, что вы можете легально получить доступ к объекту типа double
только через указатели типа double
,
Один из приемов, который часто используется для набора текста, заключается в использовании union
, Юридический в C
однако это запрещено в C ++, но некоторые компиляторы позволяют это. В вашем случае, однако, я не думаю, что есть способ использовать союз.
Идеальной ситуацией было бы сделать тяжелая атлетика на double*
данные напрямую. Если это возможно в вашем рабочем процессе.
В других ответах упоминаются реальные проблемы (преобразование может работать некорректно). Я добавлю небольшую проверку времени выполнения, которая проверяет, работает ли псевдоним, и предоставлю местозаполнитель, где вы будете вызывать / использовать свой код с интенсивным копированием.
int aliasing_supported_internal() {
double testvec[6];
_vector3* testptr = (_vector3*)(void*)testvec;
// check that the pointer wasn't changed
if (testvec != (void*)testptr) return 0;
// check for structure padding
if (testvec+3 != (void*)(testptr+1)) return 0;
// TODO other checks?
return 1;
}
int aliasing_supported() {
static int cached_result = aliasing_supported_internal();
return cached_result;
}
Этот код преобразует небольшой массив значений типа double в массив псевдонимов структур (не копирует), а затем проверяет, является ли он допустимым. Если преобразование работает (функция возвращает 1), вы, вероятно, сможете использовать такой же псевдоним (преобразование через указатель void) самостоятельно.
ОСТЕРЕГАЙТЕСЬ, ЧТО КОД МОЖЕТ БЫТЬ ПРЕКРАЩЕН НЕОЖИДАННЫМ СПОСОБОМ. Строгие правила псевдонимов гласят, что даже вышеуказанная проверка является неопределенным поведением. Это может работать или может ужасно потерпеть неудачу. Только преобразования, которым разрешено работать должным образом, должны аннулировать * и возвращаться к исходному типу указателя. Кроме того, вышеупомянутая проверка может быть полностью неверной в множественных иерархиях наследования или с виртуальными базовыми классами (в некотором смысле небезопасно преобразовывать в void *, поскольку фактическое значение указателя может быть смещено, то есть может измениться из-за ограничений, отличных от выравнивания и довольно много байт)