Link to home
Start Free TrialLog in
Avatar of Member_2_1001466
Member_2_1001466

asked on

const class & versus const class in function parameter

I found a strange error I don't really understand. I have a derived class with a virtual function. The second parameter to the call was a const reference to a class I defined in a separate dll. When compiling and debugging it I found out that inside the function not the address of the variable was known to the function but the address of the vftable of that class was used instead. Not only the values where different but even the stack got corrupted (__chkesp failed). Just buy chance I removed the reference and now everything works as expected. When am I allowed to pass a variable which doesn't change as const and by reference? At least in a copy contructor I am.
Avatar of AlexFM
AlexFM

Can you show some code?
Hey I think u can pass a const reference to a derived virtual function.

I tried with it and it works fine

Post some example of your code, so that we can investigate
Avatar of Member_2_1001466

ASKER

The class definition for the object to be passed is

class COORDDLL_API WorldCoord  
{
public:

[snip]

      WorldCoord();
      WorldCoord (const WorldCoord& wcPos);
      WorldCoord (double du, double dv, double dw);
      //WorldCoord (const WORLD_COORD mcPos);
      virtual ~WorldCoord();

      WorldCoord& operator= (const WorldCoord &wcPos);

      double m_dU;
      double m_dV;
      double m_dW;
};

The virtual class definition with the function is

class WorldCoord;
class FitObj
{
public:
      FitObj () {};
      virtual ~FitObj () {};
      virtual void ToWorldFit (WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0) = 0;
      virtual void SetPhi (double dPhi) = 0;
      virtual void SetCamPos (const WorldCoord wcPos) = 0;      // for sensor camera 1 pos and for chip rotation center
      virtual void SetOffset (const WorldCoord wcPos) = 0;      // for sensor offset from global center and for chip offset from rotation center
      virtual void SetChi2 (const double dChi2) {m_dChi2 = dChi2; return;};
      virtual double GetChi2 (void) {return m_dChi2;};
protected:
      double m_dChi2;
};

And the actual class calling the function:

typedef list<MotorCoord> MList;
typedef list<WorldCoord> WList;
#include "FitObj.h"
class CSensor : public FitObj// : public CObject
{
public:
      virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0); // virtual from class FitObj  
 [snip]
      void ToSensor(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0);
      void ToSensor(WorldCoord& wcPos, const double dPhi = 0);
      void ToWorld(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0);
      void ToWorld(WorldCoord& wcPos, const double dPhi = 0);
      void FitCoord (int n);
      CSensor();
      virtual ~CSensor(); // virtual from class FitObj  

protected:
      MotorCoord m_mcFirst;

      list<LONG>      m_clChipRot;      //< list of rotation angles of chips
      WList      m_clChipPos;      //< list of points to place chips
      WList      m_clRef;            //< list of reference points on sensor
      MList      m_clCoord;            //< list of measured motor coords of ref points
      POINT      m_pPtsFrame[5];                                          //< list of border frame points.

      MotorCoord m_mcToHead;
      MotorCoord m_mcMeasPos;
      WorldCoord m_wcOffset;
      double m_dPhi;
};

The function call with the problem was
      virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi = 0); // virtual from class FitObj  

When called via
      fpFitObj->ToWorldFit (wcPos, wcAct, phik);      // wcAct = cam1pos      // for CChip +phik is correct solution!
where fpFitObj is a pointer to the class FitObj.
Looks OK except the line:

public:
    virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0); // virtual from class FitObj  

there is no & after const WorldCoord (I beleive this is typo).
So, when you call this function, it fails. And When you replace with:

    virtual void ToWorldFit(WorldCoord wcPos, WorldCoord wcAct, const double dPhi = 0); // virtual from class FitObj  

it works?
No I first tried it with
    virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi = 0);
in FitObj and CSensor and this gave problems that inside CSensor::ToWorldFit () the address of wcAct was the WorldCoord::vftable instead of the address. I tried afterwards the version:
    virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0);
and then everything is working correct. But I don't really understand why I can't use the first function.
Avatar of Axter
FYI:
The decendant virtual functions should not have default values.
Whatever code u have, I have simulated it into a simpler version.
Just go thru this, this code works fine


===========

#include <iostream>
#include <stdlib.h>

using namespace std;

class Obj {
} ;

class A {
  public :
    virtual void f ( const Obj &p ) {
      cout << "\nIn A" ;
    }
} ;

class B : public A {
  public :
    void f ( const Obj &p ) {
      cout << "\nIn B" ;
    }  
} ;

int main(int argc, char *argv[])
{
  A *aPtr ;
  A aObj ;
  B bObj ;
  Obj o ;
 
  // Ptr points to an object of type A
  aPtr = &aObj ;
  aPtr -> f ( o ) ;

  // Ptr points to an object of type B
  aPtr = &bObj ;
  aPtr -> f ( o ) ;
  system("\nPAUSE");      
  return 0;
}

=================
SOLUTION
Avatar of Axter
Axter
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
In the above code, foo_decs has over writen the GetValue function, so it should return a 5

However, when foo_decs object is passed to SomeFunction_bad_via_value function, the out put result of GetValue is 2, because the base class function gets called instead of the decendant class function.

When ever you have a virtual class, you should always setup you're functions to pass the class by reference or by pointer.
Hi Axter,

I think your example illustrates a good point, but u have decalred a variable of type of derived class

However the real benefit of virtual functions appear when we declare a pointer of base class point to object of either the base of derived class.

Also, his code also signifies the same thing.

I tried to simulate his example by simple classes, I think it works fine

I have pasted the code above
@Axter,
No WorldCoord is not a virtual class. FitObject is a pure virtual class and CSensor is the class derived of. WorldCoord is a class which is inside a DLL.

@Sys_Prog
I assumed that I can pass a const ref but why does it fail here?

@all:
I tried using
    virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi = 0);
Now inside CSensor::ToWorldFit () wcAct is not properly passed. In the debugger I look at &wcAct and it shows
00384100 WorldCoord::`vftable` instead of something line 0012e15c (a memory location on the stack). And more severe I get the error message that ESP has not been saved correctly around a function call. All this disappears when I just change the function definition to
    virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord wcAct, const double dPhi = 0);
and recompile.

Where can I look for the reason?
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I m using MS VC++ 6.0 with SP5. I have some other places where I can pass arguments as const refs. Escpecially copy constructors need it. But even other functions don't fail that badly. Could it be project/compiler settings used for the main app and the dlls? Some code has to reside in a DLL to avoid name conflicts with MFC. Maybe compiler/linker switches could be wrong but I tried to get them all identical. Any hints here?
I guess there might be something related to settings with the dll

We can test one thing

Just try doing a simple code [as illustrated above] in your environment, then try it with a dll for the Obj class

That's what I can suggest

I will try to investigate the same
>>No WorldCoord is not a virtual class. FitObject is a pure virtual class and CSensor is the class derived of. WorldCoord is a class which is inside a DLL.

WorldCoord is a virtual class, because it has a virtual member function.
Look at the destructor.

virtual ~WorldCoord();

That's a virtual destructor.  A class with any virtual function becomes a virtual class.
@Axter,
I made WorldCoord now non virtual by removing the empty destructor.

Now &wcAct is inside the function the first data element, not the vftable anymore. This means to me, that the var is put by value onto the stack and is interpreted as address by the function. So I should blame the calling function putting the value (from assembly it seems to be a temporary copy of it) and not address onto the stack. But why does it fail for a const & and not for a simple ref? Do I need some more operators/functions for the class WorldCoord?
>> __chkesp failed
Seems to suggest different calling conventions being used. Just a thought.
@ _ys_
Yes, but why? When removing the ref from the const arg everything is fine. Otherwise everything is C++ using only calls without a modifier.
Can you post the implementation for the function you're referring to?
void CSensor::ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi /* = 0. */)
{      
      //*      correct version for fitting measured points on sensor.
      wcPos = m_wcOffset - wcPos;
      wcPos.Rotate (-m_dPhi);      // sensor rotation only depends on internal angle
      wcPos.m_dU += wcAct.m_dU;
      wcPos.m_dV += wcAct.m_dV;
      //*/
      return;
}

and the calling function
Double_t TSpecialFit::Chisquare(Int_t& npar, Double_t* par)
{
      if (fNpoints <= 0)
            return 0.;

      Double_t chisq = 0;
      Int_t n1 = fNpoints / fNrefs + 1;
      Int_t i,j,k;

      Double_t tx1, ty1;//, tx2, ty2;
      Double_t phik;
      WorldCoord wcPos, wcPos1, wcPos2, wcAct;      // current pos and camera pos
      MotorCoord mcPos;

      for (k=0;k<fNpoints;++k) {
            i = k % fNrefs;
            j = k / fNrefs;
            phik = fPhi[k] / 1000 * deg2rad;
            // Step 1

            wcPos.m_dU = fX2[k];
            wcPos.m_dV = fY2[k];
            wcPos.m_dW = 0;

            wcPos1.m_dU = par[0];
            wcPos1.m_dV = par[1];
            fpFitObj->SetOffset (wcPos1);
            wcPos1.m_dU = par[3];
            wcPos1.m_dV = par[4];
            fpFitObj->SetCamPos (wcPos1);

            switch (fiType) {
            case 3: // sensor
            case 5: // mask
                  // since ToWorld with wcAct needs to be used wcAct is set to Cam1Pos
                  wcAct.m_dU = par[3];
                  wcAct.m_dV = par[4];
                  wcAct.m_dW = 0;
                  break;
            default:
                  wcAct.m_dU = 0;
                  wcAct.m_dV = 0;
                  wcAct.m_dW = 0;
                  break;
            }
            fpFitObj->SetPhi (par[2]);
            fpFitObj->ToWorldFit (wcPos, wcAct, phik);      // wcAct = cam1pos      // for CChip +phik is correct solution!
            if ((fiType != 3) && (fiType != 5)) {
                  wcPos += wcPos1;      // subtract position of camera 2 relative to rotation center at 0/0
            }
            mcPos.ToMotor (wcPos); //, tCal);

            tx1 = fX1[k] - mcPos.m_lX;
            ty1 = fY1[k] - mcPos.m_lY;

            chisq += tx1*tx1+ty1*ty1;
      }
      return chisq;
}
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Please post type for Double_t
It looks like the TSpecialFit::Chisquare function is using the wrong variable.
It's using a variable fNpoints instead of input variable npar.

npar is not used in the function at all.

par is being indexed, but there's nothing to verify that the constant index's are not going pass the boundry of the par array.
I think the problem is being cause by logic problems in the TSpecialFit::Chisquare function.
I believe memory is getting currupt before entering the CSensor::ToWorldFit function.
@Axter
The class TSpecialFit is part of a DLL handling an interface to ROOT http://root.cern.ch. I just modified an example, but your right I don't check whether the index for par is in range. On the other hand the number or parameters are set in another member function of TSpecialFit and all are defined by myself. So I know what I am doing here (and hope its correct). npar is the number of free parameters and therefor not helpful to check the index. For the chisquare I need all parameters even the fixed ones. fNpoints is another member variable with the number of points in fX1, fX2, fY1, and fY2 indexed by variable k.

What makes me think that ist really the call to ToWorldFit is that using
const WorldCoord wcAct
the program does its work.
When using
const WorldCoord& wcAct
the address of wcAct is the first member in wcAct and not its memory location and the difference between ESP and ESI in __chkesp is sizeof (WorldCoord) - sizeof (WorlCoord*) which means to me a value has been put on the stack and the function is looking for the address only.

Double_t is a 8 byte float on all compilers known to ROOT. For VC++ 6.0 its equal to a plain double.

@all
Thanks for all your suggestions. I will do the suggested tests later and report if this helps (alignment?).
@Sys_Prog
I tested your example code without any problems. I even replaced

    virtual void f ( const Obj &p ) = 0 ;

with

    virtual void (WorldCoord& wc1, const WorldCoord& wc2)

and everything works ok. I will try to get closer by using FitObj and CSensor for a next test.
It took me a while to do some testing with partly reduced code and simple examples. My finding is now:

the call

      fpFitObj->ToWorldFit (wcPos, wcAct, phik);

defined as

 virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi);

gets not properly compiled it seems. I had a look at the assembly of it in the debugger. The double dPhi is put on the stack, then a copy of wcAct is taken and put on the stack by value followed by the address of wcPos. The called function finds now
- the address of wcPos (4 bytes) OK
- wcAct as 24 bytes of 0; should be address
- and the value of dPhi (8 bytes). OK
but it interpretes the first 4 bytes as the address of wcPos; OK.
the next 4 bytes as the address of wcAct; very bad = 0!
and the next 8 bytes as the value of phik; here 0 instead of -0.17!

Can any #pragma in headers change this behaviour?
Any suggestions for compiler options to look at?
 I've compared all under C/C++ and Link and they agree.
Hi,

I just split the points for the help figuring out some bugs in the code.
Though, the problem still is not solved.

Thanks anyway
Meanwhile the bug is found: The DLL code was linking to an include file of an old version of the main application. The main application meanwhile has evolved. The DLL was using the old function declaration and for the main app I changed it. And only if either function matched the executable ran.

No chance for experts to find the bug, since this part I omitted the part where that header was included.