Vtable

Hello!

I have created a class which inherits from TLorentzVector and another which inherits from TObject. I can compil this class into a .o file easily but when I try to link this object when creating an executable I get an error

undefined reference to vtable for ZEvent

The class is all contained in the .hpp file which is as follows:

class ZJet : public TLorentzVector
{
public:

  explicit ZJet() : TLorentzVector() { Clear(); }
  //virtual ~ZJet()                    { Clear(); }
  ZJet::~ZJet()                    { Clear(); }

  //ZJet(ZJet const&) { } //copy constructor???
  //float ntrk;

  void Clear( const Option_t* = "" )
  {
     (*this) *= 0.; // this will reset the TLorentzVector to (0.,0.,0.,0.)
     //ntrk=-999;
     for(size_t i = 14; i < 22; ++i ) ResetBit(BIT(i));
  }
  
  friend std::ostream &operator << ( std::ostream &s, const ZJet &d )
  { d.Dump(); return s; }
  
  ClassDef(ZJet,1)

};

class ZEvent : public TObject
{
public:

  typedef std::vector<float> Vfloat;
  typedef ZJet               Jet;

  //-- PV
  float npvs, pv_x, pv_y, pv_z, pv_mbprob, pv_ntracks, pv_sumpt;

protected:

  //-- the -> flag in comments helps to improve TCLonesArray I/O operations
  TClonesArray *fJets;     //->

public:

  explicit ZEvent() :
     TObject()
  {
     fJets    = new TClonesArray( Jet::Class()->GetName(),10 );
     Clear();
  }

  //virtual ~ZEvent()
  ZEvent::~ZEvent()
  {
     Clear();
     SafeDelete( fJets );
  }

  Jet&          AddJet()                    { return *( new ( (*fJets) [ NbJet() ] ) Jet() ); }
  size_t        NbJet() const               { return fJets->GetEntriesFast(); }
  const Jet&    GetJet( size_t i ) const    { return *( (Jet*) fJets->At( i ) ); }
  Jet&          GetJet( size_t i )          { return *( (Jet*) fJets->At( i ) ); }

  size_t GetNJets( CStr& option ) { return GetJets( option ).size(); }


  std::vector<int>
     GetJets( CStr& option )
     {
        std::vector<int> jets;
        if( option == "loose" ) {
           for( size_t i = 0; i < NbJet(); ++i ) { if( GetJet(i).IsLoose() ) jets.push_back(i); }
        } else if( option == "tight" ) {
           for( size_t i = 0; i < NbJet(); ++i ) { if( GetJet(i).IsTight() ) jets.push_back(i); }
        } else  {
           for( size_t i = 0; i < NbJet(); ++i ) { jets.push_back(i ); }
        }
        return jets; 
    }

  // Clean-up whole event
  void Clear( Option_t * = "" )
  {

// Rest all variables to default values
npvs = pv_x = pv_y = pv_z = pv_mbprob = pv_ntracks = pv_sumpt = 0.;

     //-- The "C" option will force each TClonesArray elements to call their own
     //   Clear() method. So we have full control of the event cleaning from here
     if( fJets    ) fJets   ->Clear("C");
  }  

  friend std::ostream &operator << ( std::ostream &s, const ZEvent &d )
  { d.Dump(); return s; }

  ClassDef(ZEvent,1)

};

I do not have any virtual functions that can be causing this vtable error. Any ideas?

Thanks!

[quote]I do not have any virtual functions that can be causing this vtable error. Any ideas?[/quote]Actually you do, both via the inheritance and via the (required here) use of the ClassDef macro. Both imply that you must create (via rootcint or genreflex), compile and link in a dictionary for your classes.

Cheers,
Philippe.

Thanks!

I wanted to avoid using cint but I guess now I have to…

gmake
g++ -pthread -m32 -I/d0usr/products/root/Linux-2-4/v5_22_00a-GCC_3_4_3-dzero-rh7/include -c main.C
g++ -pthread -m32 -I/d0usr/products/root/Linux-2-4/v5_22_00a-GCC_3_4_3-dzero-rh7/include -c ZEvent.cpp
Generating dictionary …
g++ -pthread -m32 -I/d0usr/products/root/Linux-2-4/v5_22_00a-GCC_3_4_3-dzero-rh7/include -c ZDict.cxx
Making a shared Library…
g++ -shared -Wl,-soname,ZEvent.so -o ZEvent.so -L/d0usr/products/root/Linux-2-4/v5_22_00a-GCC_3_4_3-dzero-rh7/lib -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -pthread -lm -ldl -rdynamic ZEvent.o ZDict.o
Linking run …
g++ main.o -L./ZEvent.so -L/d0usr/products/root/Linux-2-4/v5_22_00a-GCC_3_4_3-dzero-rh7/lib -lCore -lCint -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lTree -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -pthread -lm -ldl -rdynamic -lm -lcurses -o run
main.o(.gnu.linkonce.t._ZN6ZEventC1Ev+0x17): In function ZEvent::ZEvent()': : undefined reference to vtable for ZEvent’
main.o(.gnu.linkonce.t._ZN6ZEventC1Ev+0x22): In function ZEvent::ZEvent()': : undefined reference to ZJet::Class()’
main.o(.gnu.linkonce.t._ZN6ZEventC1Ev+0x96): In function ZEvent::ZEvent()': : undefined reference to ZEvent::Clear(char const*)’
collect2: ld returned 1 exit status
gmake: *** [run] Error 1

Here is the code:
ZEvent.hpp

#ifndef ZEvent_h
#define ZEvent_h

#include "TROOT.h"
#include "TRint.h"
#include "TObject.h"
#include "TClass.h"
#include "TClonesArray.h"
#include "TLorentzVector.h"

#include <iostream>
#include <vector>

//---------------------------------------------------------------------------------------
//-- TObject has a status word of 32 bits; ROOT uses internally bits 0-13 and 24-32
//   users' can use bit range 14-23 ( if not used by any other ROOT class )
//   See: http://root.cern.ch/root/html/src/TObject.h.html
//---------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------

class ZJet : public TLorentzVector
{
   public:

      explicit ZJet() : TLorentzVector() { Clear(); }
      virtual ~ZJet()                    { Clear(); }

      void ZJet::Clear( const Option_t* = "" );

      //ZJet(ZJet const&) { } //copy constructor???
      //float ntrk;

      friend std::ostream &operator << ( std::ostream &s, const ZJet &d )
      { d.Dump(); return s; }

      ClassDef(ZJet,1)
};
//---------------------------------------------------------------------------------------

class ZEvent : public TObject
{
   public:

      typedef std::vector<float> Vfloat;
      typedef ZJet                Jet;
      typedef TLorentzVector       TLV;
      typedef const TString        CStr;

      //-- PV
      float npvs, pv_x, pv_y, pv_z, pv_mbprob, pv_ntracks, pv_sumpt;

   protected:

      //-- the -> flag in comments helps to improve TCLonesArray I/O operations
      TClonesArray *fJets;     //->

   public:

      explicit ZEvent() :
	 TObject()
      {
	 fJets    = new TClonesArray( Jet::Class()->GetName(),10 );
	 Clear();
      }

      virtual ~ZEvent()
      { 
	 Clear(); 
	 SafeDelete( fJets );
      }

      void ZEvent::Clear( const Option_t* = "" );

      Jet&          AddJet()                    { return *( new ( (*fJets) [ NbJet() ] ) Jet() ); }
      size_t        NbJet() const               { return fJets->GetEntriesFast(); }
      const Jet&    GetJet( size_t i ) const    { return *( (Jet*) fJets->At( i ) ); }
      Jet&          GetJet( size_t i )          { return *( (Jet*) fJets->At( i ) ); }

      size_t GetNJets( CStr& option ) { return GetJets( option ).size(); }

      std::vector<int>   
	 GetJets( CStr& option )
	 { 
	    std::vector<int> jets;
	    for( size_t i = 0; i < NbJet(); ++i ) { jets.push_back(i ); }
	    return jets; 
	 }

      friend std::ostream &operator << ( std::ostream &s, const ZEvent &d )
      { d.Dump(); return s; }

      ClassDef(ZEvent,1)
};

//---------------------------------------------------------------------------------------

#endif

ZEvent.cpp

#include "ZEvent.hpp"

ClassImp(ZJet);
ClassImp(ZEvent);

//---------------------------------------------------------------------------------------

void ZJet::Clear( const Option_t*)
{
   (*this) *= 0.; // this will reset the TLorentzVector to (0.,0.,0.,0.)
   //ntrk=-999;
   for(size_t i = 14; i < 22; ++i ) ResetBit(BIT(i));
}

//---------------------------------------------------------------------------------------

// Clean-up whole event
void ZEvent::Clear( Option_t *)
{
   // Rest all variables to default values
   npvs = pv_x = pv_y = pv_z = pv_mbprob = pv_ntracks = pv_sumpt = 0.;

   //-- The "C" option will force each TClonesArray elements to call their own
   //   Clear() method. So we have full control of the event cleaning from here
   if( fJets    ) fJets   ->Clear("C");
}

//---------------------------------------------------------------------------------------

ZEvent_linkdef.h

#include "ZEvent.hpp"

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;

#pragma link C++ class ZJet+;
#pragma link C++ class ZEvent+;

#endif

main.C

#include "ZEvent.hpp"

using namespace std;

int main() {
   cout << "Start" << endl;
   ZEvent *ze = new ZEvent();
   cout << "End" << endl;
   return 0;
}

Makefile

######################
### Edit this part ###
######################

#ROOTSYS       = $(ROOTSYS)
CXX           = g++
LD            = g++

#############################
### Do NOT edit this part ###
#############################

CXXFLAGS      = $(shell ${ROOTSYS}/bin/root-config --cflags)
LDFLAGS       = 

ROOTLIBS      = $(shell root-config --libs)
LIBS          = $(ROOTLIBS) -lm -lcurses

HDRS          = ZEvent.hpp
SRCS          = main.C ZEvent.cpp ZDict.cxx
OBJS          = main.o ZEvent.o ZDict.o

PROGRAM       = run

$(PROGRAM):     $(OBJS) sharedlib
	@echo "Linking $(PROGRAM) ..."
	@echo $(LD) $(LDFLAGS) main.o -L./ZEvent.so $(LIBS) -o $(PROGRAM)
	@$(LD) $(LDFLAGS) main.o -L./ZEvent.so $(LIBS) -o $(PROGRAM)
	@echo "done"

sharedlib: $(OBJS)
	@echo "Making a shared Library..."
	$(LD) -shared -Wl,-soname,ZEvent.so -o ZEvent.so $(ROOTLIBS) ZEvent.o ZDict.o

##########################################################
### Only edit this part if you wish other dependencies ###
##########################################################

ZEvent.o: ZEvent.hpp

ZDict.cxx: ZEvent.hpp
	@echo "Generating dictionary ..."
	@$(ROOTSYS)/bin/rootcint ZDict.cxx -c ZEvent.hpp

%.o : %.C
	$(CXX) $(CXXFLAGS) -c $<

%.o : %.cpp
	$(CXX) $(CXXFLAGS) -c $<

%.o : %.cxx
	$(CXX) $(CXXFLAGS) -c $<

clean:
	rm ZDict.* *.o *.so
# DO NOT DELETE

Hi,

The only think I can easily see is that you used -m32 on the compilation line but not on the link lines. If this does not solve the problem, please upload a single complete tar file reproducing the problem.

Cheers,
Philippe.

I might be generating the library incorrectly since when I try to open a root session and load it I get errors such as

root [0] gSystem->Load(“ZEvent.so”);
dlopen error: /macros/test2/./ZEvent.so: undefined symbol: _ZN4ZJet11ShowMembersER16TMemberInspectorPc

…?

Good point! Adding -m32 to the LDFLAGS does not help though

I have uploaded a tar file.
zevt_test.tar.gz (2.04 KB)

Hi,

There is several issue.

The link line says “-L./ZEvent.so” which means please look for libraries in a directory named ./ZEvent.so which is obviously not what was meant. Use “ZEvent.so” instead. Alternatively you can use “-L. -lZEvent” If you rename the library to libZEvent.so.

The rootcint line is:@$(ROOTSYS)/bin/rootcint ZDict.cxx -c ZEvent.hppwhich generate the dictionary only for ZEvent, you need @$(ROOTSYS)/bin/rootcint ZDict.cxx -c ZEvent.hpp ZEvent_linkdef.h

In ZEvent.cpp there is :void ZEvent::Clear( Option_t *)which must be[void ZEvent::Clear( const Option_t *)[/code] to be consistent with the header file.

In the header file there is void ZJet::Clear( const Option_t* = "" ); void ZEvent::Clear( const Option_t* = "" );which should be void Clear( const Option_t* = "" ); void Clear( const Option_t* = "" ); to be ‘standard’ C++.

Cheers,
Philippe.

Wonderful!!

This all works great. If only I can get TTree::MergeTrees to work with a set of trees containing this class Ill be all set…

Thanks for all your help!

[quote] If only I can get TTree::MergeTrees to work with a set of trees containing this class Ill be all set…[/quote]Do you still have problem with this?

Philippe.

Hello Philippe,

No I looked into the MergeTrees function and took the parts I need to make it work.

Cheers