Dictionary generation for std::unique_ptr<> Error Root 6.12.07


_ROOT Version: 6.12.07 with CMS patches
Platform: SL6/SL7
Compiler: gcc 7


Changing std::auto_ptr to std::unique_ptr in class_def.xml gives this error message:
Error: It is not necessary to explicitly select class unique_ptr<gen::PdfInfo,default_deletegen::PdfInfo >. I/O is supported for it transparently.

CMS PR producing error

Removing std::unique_ptr from classes_def.xml results in error when reading back root file which makes use of dictionary generated without std::unique_ptr:

Log from unit test:
https://cmssdt.cern.ch/SDT/jenkins-artifacts/pull-request-integration/PR-24565/30469/unitTests.log

----- Begin Fatal Exception 18-Sep-2018 08:14:48 CEST----------------------- An exception of category ‘DictionaryNotFound’ occurred while [0] Constructing the EventProcessor [1] Calling ProductRegistry::initializeLookupTables Exception Message: No data dictionary found for the following classes: __uniq_ptr_impl<gen::PdfInfo,default_delete<gen::PdfInfo> > default_delete<gen::PdfInfo> tuple<gen::PdfInfo*,default_delete<gen::PdfInfo> > unique_ptr<gen::PdfInfo,default_delete<gen::PdfInfo> > Most likely each dictionary was never generated, but it may be that it was generated in the wrong package. Please add (or move) the specification ‘<class name=“whatever”/>’ to the appropriate classes_def.xml file along with any other information needed there. For example, if this class has any transient members, you need to specify them in classes_def.xml. Also include the class header in classes.h A type listed above might or might not be the same as a type stored in the Event (or Lumi or Run). Instead it might be the type of a data member, base class, wrapped type, or other object needed by a stored type. Below is some additional information which lists the stored types associated with the types whose dictionaries were not found:

Hi Patrick,

I propose to get rid of the CMSSW layer to better figure out the problem.
How is the class you are trying to persistify done? In principle, if you have a unique_ptr as a data member, the dictionary which is necessary is the one for T since ROOT treats unique_ptr and T* the same way.

Cheers,
Danilo


namespace gen {
   struct PdfInfo {
      std::pair<int, int>     id;
      std::pair<double, double>  x;
      std::pair<double, double>  xPDF;
      double            scalePDF;
   };
}

class GenEventInfoProduct {
   typedef gen::PdfInfo PDF;
   std::unique_ptr<PDF> pdf_;
}

classes.h

namespace SimDataFormats_GeneratorProducts {
   struct dictionary {
      edm::Wrapper<GenEventInfoProduct> wgeneventinfo;
   }
}

classes_def.xml

 <class name="GenEventInfoProduct" ClassVersion="12">
  <version ClassVersion="12" checksum="4234939286"/>
 </class>

The error message in the unit test is coming from CMS code. We will try to figure out why
initializeLookupTables() is trying to find a dictionary for std::unique_ptr;

Hi Patrick,

on my side, I built a simple reproducer which seems to work as expected. It needs root and 3 files*.

Does it work for the ROOT associated to the CMSSW build you are using?

Cheers,
D

classes.h

#include <memory>

namespace gen {
   struct PdfInfo {
      std::pair<int, int>     id;
      std::pair<double, double>  x;
      std::pair<double, double>  xPDF;
      double            scalePDF;
   };
}

class GenEventInfoProduct {
   public: // added for the example
   typedef gen::PdfInfo PDF;
   std::unique_ptr<PDF> pdf_;
};

classes.xml

<rootdict>
   <class name="gen::PdfInfo" />
   <class name="GenEventInfoProduct" />
</rootdict>

test.C

auto filename = "out.root";
// auto filename = "out.xml"; // Change to this one to produce an xml output file and easily read it

void printGenInfoProduct(const GenEventInfoProduct &g)
{
   auto &id = g.pdf_->id;
   auto &x = g.pdf_->x;
   auto &xPDF = g.pdf_->xPDF;
   auto &scalePDF = g.pdf_->scalePDF;
   std::cout << "id = (" << id.first << ", " << id.second << ")\n"
             << "x = (" << x.first << ", " << x.second << ")\n"
             << "xPDF = (" << xPDF.first << ", " << xPDF.second << ")\n"
             << "scalePDF = " << scalePDF << '\n';
}

void write()
{
   std::unique_ptr<TFile> f (TFile::Open(filename, "RECREATE"));

   GenEventInfoProduct myGenEventInfoProduct;

   // For the sake of this example
   myGenEventInfoProduct.pdf_.reset(new gen::PdfInfo());
   myGenEventInfoProduct.pdf_->id = std::make_pair(2,3);
   myGenEventInfoProduct.pdf_->x = std::make_pair(2.,3.);
   myGenEventInfoProduct.pdf_->xPDF = std::make_pair(4.,6.);
   myGenEventInfoProduct.pdf_->scalePDF = 42.;

   std::cout << "Writing \n";
   printGenInfoProduct(myGenEventInfoProduct);

   f->WriteObject(&myGenEventInfoProduct, "myGenEventInfoProduct");
   f->Close();
}

void read()
{
   std::unique_ptr<TFile> f (TFile::Open("out.root"));
   GenEventInfoProduct *myGenEventInfoProductPtr;
   f->GetObject("myGenEventInfoProduct", myGenEventInfoProductPtr);

   std::cout << "Reading \n";
   printGenInfoProduct(*myGenEventInfoProductPtr);

   f->Close();
}

void test()
{
   write();
   read();
}

The following files reproduces the error we are getting:

classes.h

#include <memory>
#include <iostream>

struct PdfInfo {
	std::pair<int, int>		id;
	std::pair<double, double>	x;
	std::pair<double, double>	xPDF;
	double				scalePDF;
};

class LHEEventProduct {
  public:
	typedef PdfInfo PDF;

	class const_iterator {
	    public:
		typedef std::forward_iterator_tag	iterator_category;
		typedef std::string			value_type;
		typedef std::ptrdiff_t			difference_type;
		typedef std::string			*pointer;
		typedef std::string			&reference;

		const_iterator() : line(npos) {}
		~const_iterator() {}

		inline bool operator == (const const_iterator &other) const
		{ return line == other.line; }
		inline bool operator != (const const_iterator &other) const
		{ return !operator == (other); }

		inline const_iterator &operator ++ ()
		{ next(); return *this; }
		inline const_iterator operator ++ (int dummy)
		{ const_iterator orig = *this; next(); return orig; }

		const std::string &operator * () const { return tmp; }
		const std::string *operator -> () const { return &tmp; }

	    private:
		friend class LHEEventProduct;

		void next();

		const LHEEventProduct	*event;
		unsigned int		line;
		std::string		tmp;

		static const unsigned int npos = 99999;
	};

	const_iterator begin() const;
	inline const_iterator end() const { return const_iterator(); }
	std::unique_ptr<PDF>		pdf_;
};

classes_def.xml

<rootdict>
   <class name="gen::PdfInfo" />
   <class name="LHEEventProduct" />
</rootdict>

test.C

#include "classes.h"
auto filename = "out.root";
//auto filename = "out.xml"; // Change to this one to produce an xml output file and easily read it


void checkClassDictionaries(std::vector<std::string>& missingDictionaries,
                         std::type_info const& typeinfo) {

    TClass* tClass = TClass::GetClass(typeinfo);
    bool result = true;

    THashTable hashTable;
    bool recursive = true;
    tClass->GetMissingDictionaries(hashTable, recursive);

    for(auto const& item : hashTable) {
      TClass const* cl = static_cast<TClass const*>(item);
      std::cout << "missing dictionary for " << cl->GetName() << "\n";
      missingDictionaries.emplace_back(cl->GetName());
      result = false;
    }
    return result;
  }




void printGenInfoProduct(const LHEEventProduct &g)
{
   auto &id = g.pdf_->id;
   auto &x = g.pdf_->x;
   auto &xPDF = g.pdf_->xPDF;
   auto &scalePDF = g.pdf_->scalePDF;
   std::cout << "id = (" << id.first << ", " << id.second << ")\n"
             << "x = (" << x.first << ", " << x.second << ")\n"
             << "xPDF = (" << xPDF.first << ", " << xPDF.second << ")\n"
             << "scalePDF = " << scalePDF << '\n';
}

void write()
{
   std::unique_ptr<TFile> f (TFile::Open(filename, "RECREATE"));

   LHEEventProduct myGenEventInfoProduct;

   // For the sake of this example
   myGenEventInfoProduct.pdf_.reset(new PdfInfo());
   myGenEventInfoProduct.pdf_->id = std::make_pair(2,3);
   myGenEventInfoProduct.pdf_->x = std::make_pair(2.,3.);
   myGenEventInfoProduct.pdf_->xPDF = std::make_pair(4.,6.);
   myGenEventInfoProduct.pdf_->scalePDF = 42.;

   std::cout << "Writing \n";
   printGenInfoProduct(myGenEventInfoProduct);

   f->WriteObject(&myGenEventInfoProduct, "myGenEventInfoProduct");
   f->Close();
}

void read()
{
   std::unique_ptr<TFile> f (TFile::Open("out.root"));
   LHEEventProduct *myGenEventInfoProductPtr;
   f->GetObject("myGenEventInfoProduct", myGenEventInfoProductPtr);
   std::vector<std::string> missingDicts;
   checkClassDictionaries(missingDicts, typeid(*myGenEventInfoProductPtr));

   std::cout << "Reading \n";
   printGenInfoProduct(*myGenEventInfoProductPtr);

   f->Close();
}

void test()
{
   write();
   read();
}

output

 root test.C        
   ------------------------------------------------------------
  | Welcome to ROOT 6.12/07                http://root.cern.ch |
  |                               (c) 1995-2017, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tag , 9 February 2018                                 |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 
Processing test.C...
Writing 
id = (2, 3)
x = (2, 3)
xPDF = (4, 6)
scalePDF = 42
Error in <TStreamerInfo::Build>: LHEEventProduct: PdfInfo has no streamer or dictionary, data member pdf_ will not be saved
missing dictionary for __uniq_ptr_impl<PdfInfo,default_delete<PdfInfo> >
missing dictionary for LHEEventProduct
missing dictionary for PdfInfo
missing dictionary for unique_ptr<PdfInfo,default_delete<PdfInfo> >
missing dictionary for tuple<PdfInfo*,default_delete<PdfInfo> >
missing dictionary for default_delete<PdfInfo>
Reading 
id = (2, 3)
x = (2, 3)
xPDF = (4, 6)
scalePDF = 42

Even with a properly generated and loaded dictionary library the messages about unique_ptr are missing from the THashTable:

root test.C
   ------------------------------------------------------------
  | Welcome to ROOT 6.12/07                http://root.cern.ch |
  |                               (c) 1995-2017, The ROOT Team |
  | Built for linuxx8664gcc                                    |
  | From tag , 9 February 2018                                 |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q' |
   ------------------------------------------------------------

root [0] 
Processing test.C...
missing dictionary for unique_ptr<gen::PDFInfo,default_delete<gen::PDFInfo> >
missing dictionary for tuple<gen::PDFInfo*,default_delete<gen::PDFInfo> >
missing dictionary for __uniq_ptr_impl<gen::PDFInfo,default_delete<gen::PDFInfo> >
missing dictionary for default_delete<gen::PDFInfo>
Writing 
id = (2, 3)
x = (2, 3)
xPDF = (4, 6)
scalePDF = 42
missing dictionary for unique_ptr<gen::PDFInfo,default_delete<gen::PDFInfo> >
missing dictionary for tuple<gen::PDFInfo*,default_delete<gen::PDFInfo> >
missing dictionary for __uniq_ptr_impl<gen::PDFInfo,default_delete<gen::PDFInfo> >
missing dictionary for default_delete<gen::PDFInfo>
Reading 
id = (2, 3)
x = (2, 3)
xPDF = (4, 6)
scalePDF = 42

Filed a Jira ticket for this
https://sft.its.cern.ch/jira/browse/ROOT-9698?filter=-2

Patrick, the fix has been merged into master and your item will be closed soon.