ClassDef ClassImp Compiler Linker failed

Hello,
I’ve been working on saving events to ROOT Trees. They’re stored in Events that go into one branch of the tree.
event.h:

#include “rootIncludes.h”
class Event:public TObject{
public:
Int_t channel;
Time_t timestamp;
UInt_t *data;
Event(int sample_length);
~Event(void);
ClassDef(Event, 1)
};

and the event.cpp:

#include “event.h”
ClassImp(Event)
Event::Event(int sample_length){
this->data = new UInt_t[sample_length];
};
Event::~Event(){
delete this->data;
};

All that was working fine (without the ClassImp and ClassDef in them) until I learned that I’d have to make dictionaries so I could read the file with the Event class. So I did .L event.cpp++ and got the event_cpp.so and the other dictionary files in my macros folder. But if I understand correctly I still need the ClassImp and ClassDef in my .h and .cpp files. However when I add those in I get

Undefined symbols for architecture x86_64:
“ROOT::GenerateInitInstance(Event const*)”, referenced from:
___cxx_global_var_init.1 in event.o
“Event::Class()”, referenced from:
Event::IsA() const in event.o
Event::ShowMembers(TMemberInspector&) const in event.o
“Event::Streamer(TBuffer&)”, referenced from:
vtable for Event in event.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I’ve gotten similar errors before and the fixes were to add -lTree, -lCore, etc to my linking flags. However I don’t know what is required for ClassDef and ClassImp.
Any help is appreciated, thanks.


ROOT Version: 6.11/02
Platform: MacOSX 10.11
Compiler: Apple LLVM 8.0 Xcode


Hi,

a couple of things.
Are you sure this datamodel works? It looks like you need to instruct root about the lenght of data to correctly persistify.
Why are you inheriting from TObject? It looks like you do not need that.
Why are you using new/delete instead of std::vector (in addition you should call delete[])? It seems that you could use std::vector.

Finally: what command are you exactly invoking to compile?

Cheers,
P

class Event works fine. The TTree filled with Event(s) writes to disk perfectly with the ClassDef and ClassImp. The issue is that when someone reads the .root file there should be a dictionary contained so I won’t have to give the event.h file to everyone. Not sure what you mean by instructing ROOT the length of the data. The size of UInt_t data is determined at runtime by passed variable. Can a dictionary only contain an object of fixed size? I’ve seen examples with arrays of variable size.
I thought I would need inheritance from TObject because it has I/O protocols, but that might not be required. It was a ditch attempt in hope that maybe ClassDef only works on TObject derivatives.
As for why the data element isn’t a vector, the data I’m reading in 1000 times/sec comes in from equipment as an array. Easy to use a std::copy instead of looping through the array and writing to a vector. Plus the graphing used when reading the tree expects UInt_t arrays. It could be changed, but it doesn’t seem required and I’m trying to keep filesize down. If there is an issue with ROOT having variable sized arrays in TTrees it can be changed.
I’m using clang++ with the following tags: -Xlinker -export_dynamic -Xlinker -no_deduplicate -stdlib=libc++ -L/Applications/root_v6.11.02/lib -lCore -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lpthread -lm -ldl -lTree -Xlinker -dependency_info -Xlinker
Pretty sure the issue is something from ROOT not being linked due to a missing flag, but I can’t find the required flag for macros. They should already be included if ROOT is running.

Thanks for the help

Hi,

class Event works fine. The TTree filled with Event(s) writes to disk perfectly with the ClassDef and ClassImp. The issue is that when someone reads the .root file there should be a dictionary contained so I won’t have to give the event.h file to everyone. Not sure what you mean by instructing ROOT the length of the data. The size of UInt_t data is determined at runtime by passed variable. Can a dictionary only contain an object of fixed size? I’ve seen examples with arrays of variable size.
I am afraid the writing is not correct. ROOT needs to know how long the array behind that pointer is. At the moment I think you are writing just the first element of the array. If you saw that, you probably saw an annotation like this:

int fMySize;
double *fMyArray; // [fMySize]

I would nevertheless go for an std::vector.
Another side remark: ClassImp is not needed for the IO but for the ROOT documentation generated through THtml: not sure this is what you wanted to achieve.

I thought I would need inheritance from TObject because it has I/O protocols, but that might not be required. It was a ditch attempt in hope that maybe ClassDef only works on TObject derivatives.

And this is a nice idea. In this particular case, this is not needed.

As for why the data element isn’t a vector, the data I’m reading in 1000 times/sec comes in from equipment as an array. Easy to use a std::copy instead of looping through the array and writing to a vector. Plus the graphing used when reading the tree expects UInt_t arrays. It could be changed, but it doesn’t seem required and I’m trying to keep filesize down. If there is an issue with ROOT having variable sized arrays in TTrees it can be changed.

I see: in this context, no, avoiding the copy is the best thing.

I’m using clang++ with the following tags: -Xlinker -export_dynamic -Xlinker -no_deduplicate -stdlib=libc++ -L/Applications/root_v6.11.02/lib -lCore -lRIO -lNet -lHist -lGraf -lGraf3d -lGpad -lRint -lPostscript -lMatrix -lPhysics -lMathCore -lThread -lMultiProc -lpthread -lm -ldl -lTree -Xlinker -dependency_info -Xlinker
Pretty sure the issue is something from ROOT not being linked due to a missing flag, but I can’t find the required flag for macros. They should already be included if ROOT is running.

Not sure here: try to remove the inheritance from TObject and then retry…

Cheers,
P

Hello,
Removed the TObject inheritance and same compilation errors as before.
Undefined symbols for architecture x86_64:
“ROOT::GenerateInitInstance(Event const*)”, referenced from:
___cxx_global_var_init.1 in event.o
“typeinfo for Event”, referenced from:
TBranch* TTree::Branch(char const*, Event*, int, int) in sis3316_energy_histogram_running.o
“vtable for Event”, referenced from:
Event::Event(int) in event.o
Event::~Event() in event.o
NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Any advice?

Those errors indicates that you have not generated the dictionary for the class Event. See ROOTUsersGuide.

Removing the TObject inheritance and ClassDef would also make those error go away but without the dictionary you would not be able to save or restore your objects.

@pcanal Thank you for the response. I’m lost on the generation part. Previously I was able to generate a event_cpp.so file that I kept in $ROOTSYS/macros, and that fixed the runtime error of Error in <TTree::Branch>: The pointer specified for events is not of a class or type known to ROOT. However I could only get my whole program to compile after removing the ClassDef and ClassImp lines, otherwise I got the errors you quoted. The errors you quoted I get on compilation, not runtime.
What is the final file structure supposed to look like? Should I have those ClassDef and ClassImp lines, a generated .so file and a Linkdef.h before I compile? And the .so file should be kept in the project folder or the $ROOTSYS/macros?
I fixed similar referenced from: vtable for Event in event.o ld: symbol(s) not found compiler errors for some GUI work by adding the flag -lGui in my build flags. Same with -lTree. Is there some flag I need to add to tell the compiler to look at the dictionary .so library files (not the ones I need to generate, the ones in $ROOTSYS/lib/***Dict.so)?
Thanks for your help.

This sounds like you used ACLiC (.L event.cpp+ or similar) to generate this file. One of the function of ACLiC is to generate the dictionary implicitly.

Instead you could have written a LinkDef.h file:

#ifdef __ROOTCLING__
#pragma link C++ class Event+;
#endif

and run

rootcling -f event_dict.cpp Event.h LinkDef.h

and compile event_dict.cpp and make it part of your library.

I thought I would need inheritance from TObject because it has I/O protocols,

The inheritance from TObject is not required for I/O.

. It was a ditch attempt in hope that maybe ClassDef only works on TObject derivatives.

ClassDef works and accelerate I/O a bit both for classes that inherits from TObject and for classes that do not.

I/O requires the generation of a dictionary (and having a ClassDef but not dictionary leads to unresolved symbols).

UInt_t *data;

for I/O of a raw pointer you must tell the I/O the size of the array:

Int_t fSampleLength;
UInt_t *data;  //[fSampleLength]

As far the further compilation/linker error, I am lost is exactly what you are trying now. Please be specific in what you are compiled and how you are linking the library.

Cheers,
Philippe.

I was able to successfully generate event_dict.cpp and event_dict_rdict.pcm manually like you suggested. Both are in my project folder. Still the same linking error on compiling as before. However, when I remove ClassDef and ClassImp I am able to compile and run the program. It executes, but I get some errors that might suggest how to fix the ClassDef/ClassImp errors.
I added

if (!TClass::GetDict(“Event”)) {
std::cout << “NO dont have an event dict” << std::endl;
}

a few lines into main(), and that line gets printed. Then when

Event currEvent;
TFile treeFile(root_Filename, “RECREATE”, “Ge76 Detector Data”) ;
TTree tree(“Detections”,“Detector events with waveform and channel information”);
tree.Branch(“events”,&currEvent);
is being executed I get the error

Error in <TCling::RegisterModule>: cannot find dictionary module event_dict_rdict.pcm
Error in <TTree::Branch>: The pointer specified for events is not of a class or type known to ROOT

Then I copied the .pcm file into my $ROOTSYS/lib the “cannot find dict module .pcm” error ceased, but the second error persisted. How do I make the generated .cpp and .pcm files part of my library? Is it more than having them in the project folder?
And I can’t compile successfully if the ClassDef and ClassImp lines are in the .h and .cpp files. I think the Error in <TTree::Branch>: The pointer specified for events is not of a class or type known to ROOT is because there isn’t a ClassDef in the event.h file, but I can’t compile the program with that line in there. I’m lost and new to this, thank you for help.

Here is the full compile command Xcode uses with the ClassDef +ClassImp lines included.

The dictionary is still not successfully created and/or loaded …

What is the content of event_dict.cpp ?

Philippe.

No errors on the rootcling command.
Here’s event_dict.cpp
Also in my previous post I had revised the Event.data to be a vector instead of a pointer to an array.

ok. First problem, the dictionary is ‘empty’. What command line did you use to generate the dictionary?

rootcling -f event_dict.cpp event.h LinkDef.h executed in Terminal. I did not type root and go into the ROOT environment first because then I couldn’t navigate to the project directory.
event.h

#ifndef event_h
#define event_h
#include "rootIncludes.h"
class Event{
public:
    Int_t channel;  //which channel of the ADC was triggered
    Time_t timestamp; //time the waveform was recorded
    std::vector<UInt_t> data;   //the waveform]
    Event();
    ~Event();
    ClassDef(Event, 1)   //Event class for storing waveforms
};
#endif /* event_h */

LinkDef.h:

#ifdef __CLINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ class Event+;
#endif

This part is wrong it needs to read

#ifdef __CLING__

or better

#ifdef __ROOTCLING__

You brilliant man! Fixed my runtime and compiler issues. Can’t believe it was that easy. A few final questions:
Where should the .pcm file be kept? Right now I’m copying it into the $ROOTSYS/lib to avoid Error in <TCling::RegisterModule>: cannot find dictionary module event_dict_rdict.pcm. Some other threads suggested putting it in the projectfolder/lib, doesn’t seem to work. How do most people carry these files around? They can’t be copying the .pcm it into the $ROOTSYS/lib on every computer they use.

Thank you for all your work,
Myles

Hi Myles,

The .pcm are intended to be in the same place/directory as the shared library they belong to.

Cheers,
Philippe.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.