Чтобы добавить некоторый фоновый контекст, мне нужен потокобезопасный генератор случайных чисел для использования в многопоточном коде Fortran, который должен быть кросс-компилятором и совместим с кросс-платформой. Лучший способ добиться этого — придерживаться языковых стандартов. Поэтому я хотел обернуть генераторы случайных чисел C++11 в функцию, которую я могу вызывать из фортрана, поэтому у меня есть один генератор случайных чисел на поток, каждый со своим собственным объектом состояния.
Поэтому я создал небольшой класс C++ с привязками к 3 функциям C.
#include <random>
#include <iostream>
class random {
std::mt19937_64 engine;
std::uniform_real_distribution<double> dist;
public:
random(uint64_t seed) : engine(seed), dist(0.0, 1.0) {};
double get_number() {
return dist(engine);
}
};
extern "C" {
void *random_construct(int seed) {
return new class random(static_cast<uint64_t> (seed));
}
double random_get_number(void *r) {
return static_cast<class random *> (r)->get_number();
}
void random_destroy(void *r) {
delete static_cast<class random *> (r);
}
}
Интерфейс Фортран
MODULE random
USE, INTRINSIC :: iso_c_binding
IMPLICIT NONE
INTERFACE
TYPE (C_PTR) FUNCTION random_construct(seed) &
& BIND(C, NAME='random_construct')
USE, INTRINSIC :: iso_c_binding
IMPLICIT NONE
INTEGER (C_INT), VALUE :: seed
END FUNCTION
END INTERFACE
INTERFACE
REAL (C_DOUBLE) FUNCTION random_get_number(r) &
& BIND(C, NAME='random_get_number')
USE, INTRINSIC :: iso_c_binding
IMPLICIT NONE
TYPE (C_PTR), VALUE :: r
END FUNCTION
END INTERFACE
INTERFACE
SUBROUTINE random_destroy(r) &
& BIND(C, NAME='random_destroy')
USE, INTRINSIC :: iso_c_binding
IMPLICIT NONE
TYPE (C_PTR), VALUE :: r
END SUBROUTINE
END INTERFACE
END MODULE
И небольшая программа для проверки этого.
PROGRAM random_test
USE random
IMPLICIT NONE
TYPE (C_PTR) :: rand_object
INTEGER :: count
CALL SYSTEM_CLOCK(count)
rand_object = random_construct(count)
WRITE(*,*) random_get_number(rand_object)
CALL random_destroy(rand_object)
WRITE(*,*) random_get_number(rand_object) ! Expected to segfault.
END PROGRAM
Выполнение этого показывает, что моя функция уничтожения работает неправильно, поскольку вызовы после моей функции уничтожения все еще генерируют случайные числа.
Если я изменю свою тестовую программу на
PROGRAM random_test
USE random
IMPLICIT NONE
TYPE (C_PTR), ALLOCATABLE :: rand_object
INTEGER :: count
CALL SYSTEM_CLOCK(count)
rand_object = random_construct(count)
WRITE(*,*) random_get_number(rand_object)
DEALLOCATE (rand_object)
WRITE(*,*) random_get_number(rand_object)
END PROGRAM
Теперь он производит поведение, которого я ожидал, и segfaults после DEALLOCATE. Теперь моя интуиция говорит, что это не должно работать, поскольку я никогда не буду пытаться выделить память на одном языке и освободить ее на другом. Но есть ли причина, по которой этого не должно быть? Объект C++ относится к типу POD, поэтому его память должна быть непрерывной. Затем, пока у Фортрана есть правильный адрес памяти, он должен иметь возможность тривиально освободить его. Есть ли что-то, что мне здесь не хватает?
r
является копией указателя, и присвоение ему другого адреса никак не повлияет на исходный указатель. - person François Andrieux   schedule 18.09.2018r = nullptr;
кrandom_destroy
не присвоит nullrand_object
.r
является копиейrand_object
, поэтому присвоениеr
не меняетrand_object
. - person François Andrieux   schedule 18.09.2018r
является ссылкой на объект, на которыйrand_object
ссылается yes, в том смысле, что они оба относятся к одному и тому же объекту. Ноr
не является ссылкой на саму ссылкуrand_object
. Редактировать: я не эксперт в Фортране, но это мое понимание, и оно согласуется с тем, что вы описываете. Еслиrand_object
было обнулено, тоrandom_get_number
, по всей вероятности, должен дать сбой. - person François Andrieux   schedule 18.09.2018TYPE (C_PTR), ALLOCATABLE :: rand_object
объявляет размещаемый скалярный указатель C. Это не имеет ничего общего с объектом/контейнером массива. - person francescalus   schedule 18.09.2018TYPE(C_PTR)
является эквивалентомstruct {void *}
, объясняющим, почему мои адреса fortran и C постоянно появлялись разными. К сожалению, кто-то больше не может добавить правильный ответ, так как он был ошибочно помечен как дубликат. - person user1139069   schedule 18.09.2018value
: процедура C++, которая выполняет уничтожение, явно не может обновить аргумент Fortran, который был передан по значению. Этот аспект рассматривается в новом вопросе, который я добавил в список дубликатов. - person francescalus   schedule 19.09.2018