Default constructor (TRootIOCtor) in my class

Hello ROOTers,

In my application which handles astronomical FITS files, unexpected default constructor is automatically generated for one of my classes, even though the class has a regular default constructor with no arguments.

I attache LinkDef.h, the header and the source files of the class, TFitsKeyValue. Here is a part of generated code by core/utils/src/rootcint_tmp, where TFitsKeyValue( (TRootIOCtor*)0) is used.

namespace ROOT { // Wrappers around operator new static void *new_TFitsKeyValue(void *p) { return p ? new(p) ::TFitsKeyValue( (TRootIOCtor*)0 ) : new ::TFitsKeyValue( (TRootIOCtor*)0 ); }

According to the ROOT users guide, this sort of constructor is created when a user does not prepare a default constructor. But in my case, it exits as in TFitsKeyValue.h and .cxx.

Since the constructor TFitsKeyValue( (TRootIOCtor*)0 ) is not defined in my source code nor auto-generated code, my library has undefined symbol in it.

                 U __ZN13TFitsKeyValueC1IP11TRootIOCtorEERKT_

Could anyone tell me how I can avoid this auto-generated constructor? What is wrong in my codes?
LinkDef.h (2.08 KB)
TFitsKeyValue.cxx (11.5 KB)
TFitsKeyValue.h (5.65 KB)

Any idea?

I have simplified the code so that anyone can easily find the problem. Please rename “Makefile.h” to “Makefile”, and put TFitsKeyValue.h and TFitsKeyValue.cxx into inc/ and src/, respectively.

$ make
Compiling src/TFitsKeyValue.cxx
g++ -O2 -pipe -Wall -W -Woverloaded-virtual -D_REENTRANT -pthread -m64 -I/usr/local/root/include -Wall -g -Iinc -c src/TFitsKeyValue.cxx -o src/TFitsKeyValue.o
Generating dictionary ...
rootcint -f src/RootAstroLibDict.cxx -c -p inc/TFitsKeyValue.h inc/LinkDef.h
Compiling src/RootAstroLibDict.cxx
g++ -O2 -pipe -Wall -W -Woverloaded-virtual -D_REENTRANT -pthread -m64 -I/usr/local/root/include -I. -c src/RootAstroLibDict.cxx -o src/RootAstroLibDict.o
g++ -dynamiclib -single_module -undefined dynamic_lookup -install_name /Users/oxon/tmp/libRootAstroLib.so -O2 -mmacosx-version-min=10.6 -m64  src/TFitsKeyValue.o src/RootAstroLibDict.o -o  libRootAstroLib.so
libRootAstroLib.so done
rlibmap -f -o libRootAstroLib.rootmap -l libRootAstroLib.so -d  -c inc/LinkDef.h
$ nm libRootAstroLib.so | grep " U " | grep TFits
                 U __ZN13TFitsKeyValueC1IP11TRootIOCtorEERKT_

Here is an example of “Symbol not found”.

$ root
root [0] gSystem->Load("libRootAstroLib")
root [1] TFitsKeyValue v(kFALSE)
root [2] v.AsInt()
(const Int_t)0
root [3] v.AsBool()
(const Bool_t)0
root [4] TFitsKeyValue* v2 = new TFitsKeyValue
root [5] v2->AsInt()
(const Int_t)0
root [6] v2->Clone()
dyld: lazy symbol binding failed: Symbol not found: __ZN13TFitsKeyValueC1IP11TRootIOCtorEERKT_
  Referenced from: /Users/oxon/tmp/./libRootAstroLib.so
  Expected in: flat namespace

dyld: Symbol not found: __ZN13TFitsKeyValueC1IP11TRootIOCtorEERKT_
  Referenced from: /Users/oxon/tmp/./libRootAstroLib.so
  Expected in: flat namespace

TFitsKeyValue.cxx (1.95 KB)
Makefile.h (1.51 KB)
TFitsKeyValue.h (1.96 KB)
LinkDef.h (429 Bytes)

Hi,

Well rootcint is somewhat correct, the constructor " TFitsKeyValue( (TRootIOCtor*)0 )" is indirectly declared and rootcint must prefer a constructor taking TRootIOCtor over the default constructor.

From the header file:class TFitsKeyValue : public TObject { .... template<typename T> TFitsKeyValue(const T& value); ...which of course applies to any value of T including TRootIOCtor. To avoid this problem either provide the instantiation explicit for TRootIOCtor like you did for Bool_t and Int_t or hide the template from CINT:class TFitsKeyValue : public TObject { .... #ifndef __CINT__ template<typename T> TFitsKeyValue(const T& value); #endif

Cheers,
Philippe.

Thanks a lot Philippe. It makes sense that TRootIOCtor is used due to the template.

Now I can call TFitsKeyValue::Clone() by putting your #ifndef hack, but it raises a segmentation fault. Would you tell me the solution again, please?

root [0] TFitsKeyValue* v = new TFitsKeyValue
root [1] v->AsBool()
(const Bool_t)0
root [2] v->AsInt()
(const Int_t)0
root [3] TFitsKeyValue* v2 = v->Clone()
Error in <TClass::New>: cannot create object of class TFitsPlaceHolder

 *** Break *** segmentation violation



===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================

Thread 1 (process 24081):
#0  0x00007fff8282cf6c in wait4 ()
#1  0x00007fff8284151a in system ()
#2  0x000000010010f645 in TUnixSystem::StackTrace ()
#3  0x000000010010c5d1 in TUnixSystem::DispatchSignals ()
#4  <signal handler called>
#5  0x00000001000eb265 in TClass::StreamerTObject ()
#6  0x0000000102857a5a in TBufferFile::WriteFastArray ()
#7  0x000000010292405a in TStreamerInfo::WriteBufferAux<char**> ()
#8  0x000000010285865e in TBufferFile::WriteClassBuffer ()
#9  0x00000001000757eb in TDirectory::CloneObject ()
#10 0x0000000100242659 in G__G__Base2_11_0_10 ()
#11 0x000000010087d556 in Cint::G__ExceptionWrapper ()
#12 0x000000010093ebbc in G__execute_call ()
#13 0x00000001009410db in G__call_cppfunc ()
#14 0x0000000100914f69 in G__interpret_func ()
#15 0x00000001009044e2 in G__getfunction ()
#16 0x00000001009f7f40 in G__getstructmem ()
#17 0x00000001009ee56e in G__getvariable ()
#18 0x00000001008d56c9 in G__getitem ()
#19 0x00000001008da6eb in G__getexpr ()
#20 0x00000001008c6a3a in G__define_var ()
#21 0x0000000100969cab in G__exec_statement ()
#22 0x00000001008c16cd in G__exec_tempfile_core ()
#23 0x00000001008c19e6 in G__exec_tempfile_fp ()
#24 0x0000000100977791 in G__process_cmd ()
#25 0x000000010001f3ee in TCint::ProcessLine ()
#26 0x0000000100066da3 in TApplication::ProcessLine ()
#27 0x000000010124355f in TRint::HandleTermInput ()
#28 0x0000000101241b37 in TTermInputHandler::Notify ()
#29 0x0000000101243a5d in TTermInputHandler::ReadNotify ()
#30 0x000000010010ca82 in TUnixSystem::CheckDescriptors ()
#31 0x000000010010cf76 in TUnixSystem::DispatchOneEvent ()
#32 0x00000001000a490d in TSystem::InnerLoop ()
#33 0x00000001000a7533 in TSystem::Run ()
#34 0x0000000100065657 in TApplication::Run ()
#35 0x000000010124279b in TRint::Run ()
#36 0x00000001000019a0 in main ()
===========================================================


The lines below might hint at the cause of the crash.
If they do not help you then please submit a bug report at
http://root.cern.ch/bugs. Please post the ENTIRE stack trace
from above as an attachment in addition to anything else
that might help us fixing this issue.
===========================================================
#5  0x00000001000eb265 in TClass::StreamerTObject ()
#6  0x0000000102857a5a in TBufferFile::WriteFastArray ()
#7  0x000000010292405a in TStreamerInfo::WriteBufferAux<char**> ()
#8  0x000000010285865e in TBufferFile::WriteClassBuffer ()
#9  0x00000001000757eb in TDirectory::CloneObject ()
===========================================================


Root > 

I am using SVN trunk on OS X 10.6.6.

Hi,

I suppose that you simply need to enable the I/O for TFitsPlaceHolder by changing ClassDef(TFitsPlaceHolder,0)to ClassDef(TFitsPlaceHolder,1)

Cheers,
Philippe.

I did, but got the same error.

Hi,

In addition, the declaration and the use in the default constructor of fContent is inconsistent: TFitsPlaceHolder* fContent; //-> public: TFitsKeyValue() : TObject(), fContent(0) {}The ‘//->’ tell ROOT that it can assume that fContent will never be null and always points to a valid object of always the same type (assumingly exactly TFitsPlaceHolder).

Since TFitsPlaceHolder is an abstract class, ROOT has know way to create an object of that type (i.e. the error about TClass::New) and because of the ‘->’ it also did not record the actual type /

So it will work better removing the comment:TFitsPlaceHolder* fContent; public: TFitsKeyValue() : TObject(), fContent(0) {}

Cheers,
Philippe.

Thank you again. It now works fine!

I have one more question.

According to the users guide, allocating memory to the member pointer inside the default constructor is not allowed because it causes memory leak. But if I use “//->” comment to a member pointer, it cannot be avoided to allocate a memory in the default constructor. Because you wrote

[quote]The ‘//->’ tell ROOT that it can assume that fContent will never be null and always points to a valid object[/quote] It sounds inconsistent to me. For ecample, TH1’s fFunctions is allocated inside the TH1::TH1().

Would you clarify this point, please?

[quote]According to the users guide, allocating memory to the member pointer inside the default constructor is not allowed because it causes memory leak. But if I use “//->” comment to a member pointer, it cannot be avoided to allocate a memory in the default constructor.[/quote]Yes, the documentation does not make clear that the member marked as “//->” are (by definition) excluded from this recommendation.

[quote]For ecample, TH1’s fFunctions is allocated inside the TH1::TH1().[/quote]Yes, and it is marked with ‘//->’ the definition of this marker is very clear (i.e. the default allocates it, the I/O never ever delete it or allocate it).

Reading your code, it is also clear, that you should not use //-> for that member has it is/should be allocated with different type depending of the usage.

Cheers,
Philippe.

Do you mean that no memory leak is caused when I allocate memory to a member which has “//->”?

[quote]Do you mean that no memory leak is caused when I allocate memory to a member which has “//->”?[/quote]Yes (well of course it depends and where/when etc. but in the context of the default constructor, yes).
[However //-> is not the right thing to use for your application]

Philippe.