Union and StreamerInfo

Hello

I’m using a union to have a single interface to my data disregarding their nature. Once I try to teach root to read it, I failed (from the message I got while running). Here is a mininmal working example of a class UEntry, starting with the .h, .cxx and Linkdef file

[code]#ifndef UENTRY_H
#define UENTRY_H

//std
#include
using namespace std;

//ROOT
#include “TObject.h”

class UEntry : public TObject
{

public:
enum TAG {Double, String, Vector};
TAG tag;
string name;
union pouet {
double dou;
string *str;
vector *vec;
} val;

UEntry();
UEntry(UEntry::TAG atag, string aname="[default]");
~UEntry();


ClassDef(UEntry, 200);

};

#endif[/code]

[code]#define UENTRY_CXX

#include “UEntry.h”

ClassImp (UEntry)

UEntry::UEntry():
TObject(), tag(UEntry::Double), name("[default_const]")
{
val.dou=0;
}

UEntry::UEntry(UEntry::TAG atag, string aname):
TObject(), tag(atag), name(aname)
{
switch(tag)
{
case UEntry::Double: val.dou = -1; break;
case UEntry::String: val.str = new string(""); break;
case UEntry::Vector: val.vec = new vector(); break;
}
}

UEntry::~UEntry()
{
switch(tag)
{
case UEntry::String: delete val.str; break;
case UEntry::Vector: delete val.vec; break;
}
}[/code]

[code]#ifdef CINT

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ nestedclasses;

#pragma link C++ union UEntry::pouet+;
#pragma link C++ class UEntry+;

#endif[/code]

I’m producing a library with a dummy makefile:

all: rootcint -f UEntryDict.cxx -c UEntry.h UEntryLinkDef.h g++ -fPIC `root-config --cflags --libs` -c UEntryDict.cxx g++ -fPIC `root-config --cflags --libs` -c UEntry.cxx g++ -shared -o UEntry.so UEntry.o UEntryDict.o

and once I execute it gives:

[quote]root [1] gSystem->Load(“UEntry.so”);
root [2] gROOT->GetClass(“UEntry”)->GetStreamerInfo()->ls()
Error in TStreamerInfo::Build: UEntry, unknown type: UEntry::pouet val

StreamerInfo for class: UEntry, version=200, checksum=0x38b15c0c
TObject BASE offset= 0 type=66 Basic ROOT object
UEntry::TAG tag offset= 16 type= 3
string name offset= 24 type=300 ,stl=365, ctype=365,
i= 0, TObject type= 66, offset= 0, len=1, method=0
i= 1, tag type= 3, offset= 16, len=1, method=0
i= 2, name type=300, offset= 24, len=1, method=0
[/quote]

Can anyone point me to what I’m doing wrong ?
For simplicity, I’m joining the three files in case someone wants to try
Thanks in advance
jb
UEntry.cxx (600 Bytes)
UEntry.h (436 Bytes)
UEntryLinkDef.h (221 Bytes)

As far as we know it is impossible to write a platform independent version of an union without ‘extra’ information. For example if the union isunion Inside { char fOne[4]; double fTwo; };The I/O layer has no way to know whether the user filled the ‘char[4]’ or the ‘double’, so the question is upon storing (or restoring) should we byte swap or not (assuming the file and the current machine have different endianess) … and whichever choice you make will be wrong for either fOne or fTwo … (So we might instead support the upcoming std::variant). In the case where one of the alternative is actually a pointer this is actually worse since storing a pointer mean storing the pointed to object rather that the ‘bytes’ of the pointer (so on file the two side of the union do not have the same size and must use a completely different code path … but again the I/O layer can not guess which path to use …)

Cheers,
Philippe.

Dear Philippe

Thanks for your answer. From what I get from your answer, you strongly discourage the use ot this kind of structure within a Tree for reading and writing purpose. The thing is, I’m actually not interested in neither writing these objects nor reading them. I’d like to know whether, to you, it will be ok (okish ?) to use this only as an interface, internally (for sake of simplicity and internal organisation) ? If so, would it be possible to state to my class that inherits from a ttree: “this is a an internal member which i’m not interested in writing or reading”… ?

thanks
cheers
jb

[quote] If so, would it be possible to state to my class that inherits from a ttree: “this is a an internal member which i’m not interested in writing or reading”… ?[/quote]Yes, of course :slight_smile:. You can mark the member as transient. For example by adding a comment with an exclamation mark

union pouet {
   double dou;
   string *str;
   vector<double> *vec;
    } val;  //! This will not be saved

Cheers,
Philippe.

Hello all,

what would you suggest as the simplest work-around for storing the unions? I have a legacy code where unions are present and I include those unions in my structs, which I store to root files.

Thanks,
RFK

Why not use std::variant instead? (You can’t use GCC 9 right now if you do this though, due to a ROOT bug.)

std::variant is also not yet supported in ROOT I/O.

For storing the union, the work around would depend on the content (and possibly the type of machine you hope to read the data on)