我正在尝试构建一个由类de_solver
编码的通用随机微分方程求解器,该类需要一组由model
类给出的微分方程。该模型通过类sde
提供给求解器,该类在模型和求解器之间工作接口,将原始模型的方程重新转换为确定性 + 随机方程的两个子集。
目前我的代码有几个问题,我认为必须处理继承和类型转换。特别是,我无法将指定模型方程的成员函数传递给积分器。首先假设我有一个这种类型的model
类:
class model{
...
public:
size_t N_eq;
model(...); // Assign N_eq and parameters and other stuff
...
int equations_det(double t, const double y_[], double dy_[]); // Define deterministic RHS of equations
static int equations_gsl_wrapper(double t, const double y_[], double dy_[],void *params); // A wrapper that make equations_det suitable for a GSL solver
void equations_stoch(double t,double dt,const double y_[], double dy_[],); // The stochastic RHS of the model equations
static void ode_s_wrapper(double t,const double y_[], double dy_[], void *params); // A wrapper to pass equations_det to the stochastic integrator (in the class `de_solver`).
int simulate(t,tfin,tstep); // The actual simulator which will invoke the 'de_solver'
};
正如本文所指出的,static
规范源于对模型的确定性部分使用 GSL 集成器的需要。
然后,接口类sde
如下:
class sde{
...
public:
size_t N_eq;
sde(size_t N_,
int (*dtr)(double, const double*, double*, void*),
void (*stc)(double, double, const double*, double*, void*));
int (*deterministic)(double t, const double* y_[], double* dy_[], void * params);
void (*stochastic)(double t,const double y_[], double dy_[], void *params);
//Then again, akin to the model class, use some `wrappers`
static int deterministic_wrapper(double t, const double y_[], double dy_[], void * params);
static void stochastic_wrapper(double t, const double y_[], double *dy_, void *params);
};
这个想法是在sde
类中具有从给定的模型继承的成员函数。至于两个"包装纸",我将澄清我稍后介绍它们的原因。
最后,de_solver
类是这样的:
class de_solver{
sde *sys;
public:
de_solver(sde *system); // Will initialize solver with the system put in the `sde` form
...
void integrate(void *params, double *ts, double **sol);
};
model
在单独的文件(model.H
和model.CPP
(中声明/定义,涉及sde
和de_solver
(solvers.h
和solvers.cpp
(。
这个想法是在model
类中有一个simulate
成员函数,以便
int model::simulate(double t, double tfin, double dt){
// Prepare solver
// 1. Create the `sde` object from model `sys`
sde recast_sys(NEQ, model::deterministic_wrapper, model::stochastic_wrapper);
// 2. Instantiate solver with recast system
de_solver integrator(&recast_sys);
// Run simulation
double *ts = ... // Output time instants
double **sol = ... // Output solution
void *params_base = static_cast<void*>(std::addressof(this));
integrator.integrate(params_base);
return 1; // In practice there is some error check on this return condition (omitted here for brevity)
}
总之,sys
调用了通过recast_sys
提供的模型方程的确定性和随机性部分的integrator
。由于积分器的确定性部分依赖于 GSL 求解器,因此我使用附加参数参数将指向求解器的指针传递给实际类成员函数。以这种方式,在我拥有的integrator.integrate
成员函数中(见上面提到的帖子(
de_solver::integrate(void *params_base, ...){
...
// I allocate an array of two void pointers: the first to the `model` class (assumed to be passed by `params_base`), and the second to the `sde` class
void **params = (void**)calloc(2,sizeof(void*));
params[0] = params_base;
params[1] = reinterpret_cast<void *>(std::addressof(sys)); // the recast system as private member of the sde class
gsl_odeiv2_driver * d;
gsl_odeiv2_system system = {sys->deterministic_wrapper, nullptr, sys->NEQ, params};
d = gsl_odeiv2_driver_alloc_y_new (&system, gsl_odeiv2_step_bsimp, opts.dt, opts.atol, opts.rtol);
...
}
int sde::deterministic_wrapper(double t, const double y_[], double dy_[], void * params){
assert(params);
return(static_cast<sde*>(params[1])->deterministic(t,y_,dy_,params)); // This will issue an error: ‘void*’ is not a pointer-to-object type
}
int model::equations_gsl_wrapper(double t, const double y_[], double dy_[], void * params){
assert(params);
return(static_cast<model*>(params[0])->ode_gsl(t,y_,dy_)); // This will issue an error: ‘void*’ is not a pointer-to-object type
}
从这篇文章中获取了两个指向空隙的指针数组。但是,似乎一旦在包装器中使用,它就会产生错误,可能是因为 void 数组上的算术不清楚(如此处指出的(?
目前,我无法为上面报告的错误编译代码。同样出于某种原因,编译器告诉我model.simulate
成员函数中的this
指针生成
error: use of deleted function ‘const _Tp* std::addressof(const _Tp&&) [with _Tp = model*]’
我怀疑我弄乱了静态和非静态成员函数,并且我没有正确传递它们。任何意见将不胜感激。
我还没有完全掌握你的整个问题,但答案似乎很简单:
错误:使用已删除的函数"const _Tp* std::addressof(const _Tp&&( [带有 _Tp = 模型*]">
编译器告诉您,您正在尝试获取指向实例的指针的地址,因为您确实std::addressof(this)
.this
已经是您要查找的指针(model*
(,将其直接投射到void*
。
return(static_cast<model*>(params[0])->ode_gsl(t,y_,dy_)); // This will issue an error: ‘void*’ is not a pointer-to-object type
当你这样做params[0]
你已经取消了指针一次(内置的x[y]
完全等同于*(x + y)
(,并且最终会得到一个编译器不喜欢的普通void
。您的 void 指针数组具有另一个间接级别:
int deterministic_wrapper(double t, /* ... */ void * params) {
void** voidPtrs = static_cast<void**>(params);
return static_cast<sde*>(voidPtrs[1])->deterministic(t, /* ... */ params);
}
看这里。
更好的解决方案是创建一个包含指向类的指针的struct
(您的 void 指针数组基本上就是这样,刚好足够接近三星编程,以至于您犯了上述错误(:
struct myParams
{
model* _model;
sde* _sde;
};
// ...
int deterministic_wrapper(double t, /* ... */ void * params) {
return static_cast<myParams*>(params)->_sde->deterministic(t, /* ... */ params);
}
请注意,无论如何,您都必须清理该内存。如果你分配了该数组,则需要释放它,如果你new
结构,你需要delete
它,如果你在堆栈上创建它,你需要确保它的生存时间足够长,以便所有这些函数完成。
感谢Max Langhof的建议,代码现在运行良好。我提到的可见性问题与类方法地址的错误传递有关。
在上面的代码示例中,model::simulate
我通过以下方式初始化sde recast_sys
// Prepare solver
// 1. Create the `sde` object from model `sys`
sde recast_sys(NEQ, model::deterministic_wrapper, model::stochastic_wrapper);
相反,传递我的::*_wrapper
函数地址的正确方法是通过this
指针,即
sde recast_sys(NEQ, this->deterministic_wrapper, this->stochastic_wrapper);