Solved

const class & versus const class in function parameter

Posted on 2003-10-22
29
604 Views
Last Modified: 2012-06-27
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.
0
Comment
Question by:SteH
  • 12
  • 8
  • 5
  • +2
29 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 9596985
Can you show some code?
0
 
LVL 10

Expert Comment

by:Sys_Prog
ID: 9597046
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
0
 
LVL 13

Author Comment

by:SteH
ID: 9597084
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.
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 9597187
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?
0
 
LVL 13

Author Comment

by:SteH
ID: 9597267
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.
0
 
LVL 30

Expert Comment

by:Axter
ID: 9597624
FYI:
The decendant virtual functions should not have default values.
0
 
LVL 10

Expert Comment

by:Sys_Prog
ID: 9597649
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;
}

=================
0
 
LVL 30

Assisted Solution

by:Axter
Axter earned 200 total points
ID: 9597660
Since WorldCoord is a virtual class, your functions should pass the value by reference or pointer.
So it should be the following:
virtual void ToWorldFit(WorldCoord& wcPos, const WorldCoord& wcAct, const double dPhi = 0);

Here's an example of what happens when you pass a virtual class by value compared to what you get when you pass by reference.

class foo
{
public:
      foo(){x=2;}
      virtual ~foo(){}
      virtual int GetValue(){return x;}
      int x;
};

class foo_decs : public foo
{
public:
      foo_decs(){y=3;}
      ~foo_decs(){}
      int GetValue(){return x+y;}
      int y;
};


void SomeFunction_bad_via_value(foo f)
{
      cout << f.GetValue() << endl << endl;
}

void SomeFunction_good_via_ref(foo &f)
{
      cout << f.GetValue() << endl << endl;
}

void SomeFunction_good_via_ptr(foo *f)
{
      cout << f->GetValue() << endl << endl;
}

int main(int argc, char* argv[])
{
      foo_decs fd;
      SomeFunction_bad_via_value(fd);
      SomeFunction_good_via_ref(fd);
      SomeFunction_good_via_ptr(&fd);
      
      system("pause");
      return 0;
}

0
 
LVL 30

Expert Comment

by:Axter
ID: 9597673
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.
0
 
LVL 10

Expert Comment

by:Sys_Prog
ID: 9597693
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
0
 
LVL 13

Author Comment

by:SteH
ID: 9597730
@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?
0
 
LVL 10

Accepted Solution

by:
Sys_Prog earned 200 total points
ID: 9597842
Hi SteH

I cannot figure out any problem with your code

As u know, I have coded the same thing in a simpler/illustrative way

Below is an exact replica of the situation in your code


Also regarding the value while debugging, I tried with both of the approaches - value and reference, in both ways, it shows me the same thing in debug

I think some issue with the compiler

Which one are u having

I am using a GNU compiler
===

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

using namespace std;

class Obj {
} ;

class A {
  public :
    virtual void f ( const Obj &p ) = 0 ;
} ;

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

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

=======
0
 
LVL 13

Author Comment

by:SteH
ID: 9597862
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?
0
 
LVL 10

Expert Comment

by:Sys_Prog
ID: 9597894
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
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 30

Expert Comment

by:Axter
ID: 9598070
>>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.
0
 
LVL 13

Author Comment

by:SteH
ID: 9598227
@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?
0
 
LVL 9

Expert Comment

by:_ys_
ID: 9598304
>> __chkesp failed
Seems to suggest different calling conventions being used. Just a thought.
0
 
LVL 13

Author Comment

by:SteH
ID: 9598402
@ _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.
0
 
LVL 30

Expert Comment

by:Axter
ID: 9598599
Can you post the implementation for the function you're referring to?
0
 
LVL 13

Author Comment

by:SteH
ID: 9598682
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;
}
0
 
LVL 9

Assisted Solution

by:_ys_
_ys_ earned 100 total points
ID: 9598808
Verify the alignments.
What's sizeof (wcPos)?

You could also iterate through all possible calling conventions, until it works. once we know which works (if any) we may be able to reason with it.
0
 
LVL 30

Expert Comment

by:Axter
ID: 9602059
Please post type for Double_t
0
 
LVL 30

Expert Comment

by:Axter
ID: 9602100
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.
0
 
LVL 30

Expert Comment

by:Axter
ID: 9602119
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.
0
 
LVL 13

Author Comment

by:SteH
ID: 9604929
@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?).
0
 
LVL 13

Author Comment

by:SteH
ID: 9605208
@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.
0
 
LVL 13

Author Comment

by:SteH
ID: 9650868
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.
0
 
LVL 13

Author Comment

by:SteH
ID: 9700800
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
0
 
LVL 13

Author Comment

by:SteH
ID: 13162735
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.
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Suggested Solutions

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now