0

Background

I'm writing some C++ code for a project that involves the simulation of a lot of State Space mathematical models. Because there are several different types of models that exist with varying degrees of complexity (ie non-linear/linear & time varying/non-varying) I created a polymorphic class structure that would be able to represent various features of each type while still keeping the common properties of the matrices bundled together nicely. This way I can generalize easily to various model types for simulation inside of OdeInt.

The current version of the simple base class looks like this:

class SS_ModelBase
{
public:
    virtual Eigen::MatrixXd getA() = 0;
    virtual Eigen::MatrixXd getB() = 0;
    virtual Eigen::MatrixXd getC() = 0;
    virtual Eigen::MatrixXd getD() = 0;
    virtual Eigen::MatrixXd getX0() = 0;

    virtual int getNumInputs() = 0;
    virtual int getNumOutputs() = 0;
    virtual int getNumStates() = 0;

private:
};
typedef boost::shared_ptr<SS_ModelBase> SS_ModelBase_sPtr;

Each derived class for the mentioned types then have their own variables for actually storing specific representations of the matrices A, B, C, D, & X0. The inputs, outputs, states are used for some dimensional information that might be needed for later manipulation. This has been working extremely well for me in practice.

An example of how I would use the model in the simulator is as shown:

Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt, 
    const SS_ModelBase_sPtr& model, const PID_Values pid)
{
    /* Due to how Odeint works internally, it's not feasible to pass in a full state space model object 
    and run the algorithm directly on that. (Odeint REALLY dislikes pointers) Instead, a local copy of the 
    model will be made. For now, assume that only NLTIV models are handled. */
    SS_NLTIVModel localModel(model);

    /* Copy the relevant data from the model for cleaner code in main sim*/
    Eigen::MatrixXd x = localModel.getX0();
    Eigen::MatrixXd A = localModel.getA();
    Eigen::MatrixXd B = localModel.getB();
    Eigen::MatrixXd C = localModel.getC();
    Eigen::MatrixXd D = localModel.getD();

 //Other irrelevant code that actually performs numerical simulations
}

The custom copy constructor in the above code looks like this:

/* Copy constructor */
    SS_NLTIVModel(const SS_ModelBase_sPtr& base)
    {
        inputs = base->getNumInputs();
        outputs = base->getNumOutputs();
        states = base->getNumStates();

        A.resize(states, states); A.setZero(states, states);
        B.resize(states, inputs); B.setZero(states, inputs);
        C.resize(outputs, states); C.setZero(outputs, states);
        D.resize(outputs, inputs); D.setZero(outputs, inputs);
        X0.resize(states, 1); X0.setZero(states, 1);
        U.resize(inputs, 1); U.setZero(inputs, 1);
    }

Question

I noticed in some of my simulations that I would get memory access violation errors in the copy constructor when trying to resize the various matrices. std::malloc() gets called deep in the Eigen framework and was throwing errors. After many hours pounding my head against the wall trying to figure out why the Eigen software was suddenly not working, I stumbled upon a solution, but I don't know why it works or if there is a better way.

My old broken function signature looked like this:

Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt, 
    SS_ModelBase_sPtr model, PID_Values pid)

The new working function signature looks like this:

Eigen::MatrixXd StateSpaceSimulator::stepResponse(const double start, const double stop, const double dt, 
    const SS_ModelBase_sPtr& model, const PID_Values pid)

What's the run time difference between these two that could be causing my error? I know the first will increment the reference counter for the shared_ptr and the second will not, but other than that, I'm not sure what would change the behavior enough to cause my problem...thoughts?

Brandon Braun
  • 316
  • 2
  • 12
  • I notice your `SS_ModelBase` class has no [virtual destructor](https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors). That could be a problem. Since you're using a `shared_ptr` to an abstract base class, it will be doing polymorphic deletion. – Fred Larson Jan 23 '18 at 22:28
  • Thanks for noticing that @FredLarson. I'll go ahead and add that in. Despite this, the object my shared_ptr is pointing to is very much alive when I am getting my issues. – Brandon Braun Jan 23 '18 at 22:40
  • But you have undefined behavior, and all sorts of weird things can happen. – Fred Larson Jan 23 '18 at 22:41
  • 2
    @FredLarson that is often said out misconception. The shared_pointer stores deleter that deletes most derived object (unlike unique_ptr or mundane pointer). – Öö Tiib Jan 23 '18 at 22:44
  • Very weird things indeed! You are right @FredLarson. Adding the virtual destructor fixed the issue. What's strange is that I'm fairly confident my object isn't destroyed until long after I'm done with my math...hmm. – Brandon Braun Jan 23 '18 at 22:52
  • I stand corrected, and I learned something (thank you!) But I'll stand by the assertion that you have undefined behavior. The change in your function signature shouldn't have the effect you describe. It shouldn't make any visible difference at all, really. – Fred Larson Jan 23 '18 at 22:54
  • Put a printf in your ctor and dtor. – Jonathan Potter Jan 23 '18 at 22:57
  • Yes, Fred, undefined behavior there can be. Something in that program that corrupts some objects and causes general unstability. Slight code changes may make it temporary to stop manifesting in loudly, but with bit different test data or more changes to code it is soon back again. – Öö Tiib Jan 23 '18 at 22:57
  • More info on how magical `shared_ptr` is: https://stackoverflow.com/q/3899790/10077 – Fred Larson Jan 23 '18 at 23:02
  • Holy crap, so that's why my code wasn't imploding...I have virtual base classes with no destructors alllll over the place in this project. Huh. Learn something new everyday – Brandon Braun Jan 25 '18 at 01:03

0 Answers0