其他 类 的成员函数对成员函数的嵌套引用

Nested references to member functions by member functions of other classes

我正在尝试构建一个由 class de_solver 编码的通用随机微分方程求解器,它采用 model class 给出的一组微分方程。该模型通过 class sde 提供给求解器,它在模型和求解器之间工作,在确定性 + 随机方程的两个子集中重铸原始模型的方程。

目前我的代码有几个问题,我认为必须处理继承和类型转换。特别是我无法设法将指定模型方程的成员函数传递给积分器。先说我有一个modelclass这种类型的:

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 积分器的需要,正如 所指出的那样。

然后,接口 class 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 class 中包含继承自给定模型的成员函数。至于这两个'wrappers,'我会在稍后说明为什么要介绍它们。

最后 de_solver class 是这样的:

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 相对于 sdede_solversolvers.hsolvers.cpp).

想法是在 model class 中有一个 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 调用 integrator,它作用于通过 recast_sys 提供的模型方程的确定性和随机性部分。因为积分器的确定性部分依赖于 GSL 求解器,所以我使用附加参数参数将指向求解器的指针传递给实际的 class 成员函数。以这种方式,在我拥有的 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 的指针的数组取自 this post. However, it seems that the once used in the wrapper, it produces an error maybe because arithmetic on void arrays is not clear (as pointed out here)?

目前我无法针对上面报告的错误编译我的代码。同样出于某种原因,编译器告诉我 model.simulate 成员函数中的 this 指针生成

error: use of deleted function ‘const _Tp* std::addressof(const _Tp&&) [with _Tp = model*]’

我怀疑我在乱用静态和非静态成员函数,而且我没有正确传递它们。任何输入将不胜感激。

我还没有完全理解你的整个问题,但答案似乎很简单:

error: use of deleted function ‘const _Tp* std::addressof(const _Tp&&) [with _Tp = model*]’

编译器告诉您,您正试图将 指针 的地址获取到您的实例,因为您确实 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,编译器不喜欢的。您的 array 空指针有另一个间接级别:

int deterministic_wrapper(double t, /* ... */ void * params) {
  void** voidPtrs = static_cast<void**>(params);
  return static_cast<sde*>(voidPtrs[1])->deterministic(t, /* ... */ params);
}

参见 here

更好的解决方案是只创建一个 struct 来保存指向您的 类 的指针(您的 void 指针数组基本上就是这样,刚好足够接近 three star programming 它你是不是犯了以上错误):

struct myParams
{
  model* _model;
  sde* _sde;
};

// ...

int deterministic_wrapper(double t, /* ... */ void * params) {
  return static_cast<myParams*>(params)->_sde->deterministic(t, /* ... */ params);
}

请注意,无论如何,您都必须清理该内存。如果你分配了那个数组,你需要释放它,如果你 new'd 你需要 delete 它的结构,如果你在堆栈上创建它,你需要确保它对所有这些功能完成。

感谢 Max Langhof 的建议,代码现在运行良好。我提到的可见性问题与 class 方法地址的错误传递有关。

在上面的代码示例中,在 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);