TF1 constructor broken in ROOT 6.40.02


Please read tips for efficient and successful posting and posting code

Please fill also the fields below. Note that root -b -q will tell you this info, and starting from 6.28/06 upwards, you can call .forum bug from the ROOT prompt to pre-populate a topic.

ROOT Version: 6.40.02
Platform: Ubuntu 24.04
Compiler: gcc13.3


Hi,

When I tried to compile my private classes with ROOT 6.40.02 it turned out that the TF1 constructor for memberfunctions (TF1() 12/13 in the corresponding docs) is broken compared to the version ROOT 6.38.06. Obviously this is a serious problem, since it would involve changes in user codes and breaks backward compatibility. So, I have to stick to ROOT 6.38.06 unless you provide an additional TF1 constructor that is backward compatible.

Furthermore, it turns out that rootcint can’t be used anymore in ROOT 6.38.06 whereas it can still be used in ROOT 6.34.02. In our compilation scripts we still use rootcint for backward compatibility. Is there a way to keep on using rootcint, or by what should I replace “rootcint” ?

Cheers,

Nick.

You can use rootcling (“replaces” old rootcint in ROOT 6.x).
But I do not understand why rootcint does not work for you (even in the latest ROOT 6.40, it’s still there).

I think the last commits in TF1 might explain what you are seeing. @jonas might comment ?

Thanks! This works correctly now.

Hi Olivier,

I looked into the docs, and it turns out that the constructor TF1() [14/15] from ROOT 6.38.06 is missing in ROOT 6.40.02. Obviously this will break user codes, so please add that constructor (i.e. the one with the extra two char* parameters) back again in ROOT 6.40. When I use ROOT6.38.06 everything works fine.

Hi Nick,

Yes these constructors were removed on January the 6th by this PR for @jonas. I guess he can give you some hints.

As a temporary workaround while this is solved, you might get around by doing something like:

#if ROOT_VERSION_CODE < ROOT_VERSION(6, 40, 00)
yourOldCode
#else
Use new TF1 constructor variant
#endif

Hi, thanks for the report!

I don’t plan to bring these constructors back.

Indeed, I removed these constructors with this PR, and their removal is also mentioned in the 6.40 release notes:

The TF1, TF2, and TF3 constructors for CINT compatibility were removed. This concerns the templated constructors that additionally took the name of the used functor class and member function. With ROOT 6, these names can be omitted.

The removal had a clear motivation: these constructors with the unused arguments made it even more complicated for users to figure out which of the many constructors they should use, and because const char* can also be implicitly created from zero-like literals, it was also very easy to accidentally end up with the now-removed overload by accident, resulting in wrong behavior of the code while still compiling.

Here is a little ROOT macro that illustrates the exact user story that prompted me to remove the overloads. This was seemingly absurd behavior to the user in ROOT 6.38, which is fixed in 6.40 with the char * overloads gone:

class MyFunction {
public:
   double Evaluate(double *x, double *p) { return p[0] * p[1]; }
};

void cint_ctor_trap()
{
   MyFunction obj;
   const Int_t npar = 2;

   TF1 fOK("fOK", &obj, &MyFunction::Evaluate, 0, 1, npar, 0, TF1::EAddToList::kDefault);

   // You think you can just use the literal "0" instead of a TF1::EAddToList::kDefault
   // (which also has value 0)?
   // Get ready for a surprise! The conversion to char * will hit and you get a different constructor!
   TF1 fBug("fBug", &obj, &MyFunction::Evaluate, 0, 1, npar, 0, 0);

   std::cout << fOK.GetNdim() << std::endl; // gives 0, correctly
   std::cout << fBug.GetNdim() << std::endl; // gives 1!
}

Therefore, I concluded that the breakage of existing code is the lesser evil, because at least there are clear compiler errors that one can easily fix by not passing the unused string argument. This change is completely backwards compatible too, because the overloads without the const char* argument exist since ROOT 6. So you can cleanly update your code without adding more boilerplate like ROOT_VERSION_CODE preprocessor macro checks.

We try to not break use code by removing stuff just for the fun of it, only if there is a particular reason :slightly_smiling_face:

Cheers,
Jonas

Hi Jonas,

Thanks for the explanation. This is why I always use “TString” instead of “char*” as argument in my private code. Since I don’t want to complicate my private code with options like ROOT_VERSION_CODE I spent some nights scanning and editing my private classes to make the modification, and now everything works fine again with ROOT 6.40.02.

Cheers,

Nick.

Thanks for your understanding! I’m glad it works for you now.