Issue with pragma enum class in template class

Hello,

I take my chance here, I have defined an enum class type, such that I can switch between mathematical representations easily.

Considering the reproducer below, I get the following error message:

Forward declarations from [..]/install/lib/libXXX.rootmap:3:11: error: unknown type name 'MyEnum'
template <MyEnum> class MyClass;
               ^

Here is an overview of the rootmap, MyEnum is not declared

{ decls }
[..]
template <MyEnum> class MyClass;
[..]
[ libXXX.dylib ]
[...]

How can I fix the root map ?
It seems enum class is missing in root map so MyEnum is not defined.

Here are some abstractions:

# LinkDef.h
#pragma link C++ enum class MyEnum;

#pragma link C++ class MyClass<MyEnum::E0>+;
#pragma link C++ class MyClass <MyEnum::E1>+;
#pragma link C++ class MyClass <MyEnum::E2>+;

# Header.h
enum class MyEnum { E0, E1, E2 };
template <MyEnum T = MyEnum::E0>
class MyClass : public MyClassAbstract
{
    //  [...]
}

Hi Marco,

The syntax for selecting enums in the linkdef is the following:

#pragma link C++ enum MyEnum;

That should help ROOT autoloading the library where the enum is implemented and autoparsing the header where it is defined.

Best,
D

Dear Danilo,

Thank you for your great help! It sounds like progress.
I can now see MyEnum in [..]/install/lib/libXXX.rootmap under the list of [ libXXX.dylib ] elements, but the following issue persists.

Forward declarations from [..]/install/lib/libXXX.rootmap:3:11: error: unknown type name 'MyEnum'
template <MyEnum> class MyClass;

May I ask some information about the meaning of { decls } ? It seems to be detached of the [ libXXX.dylib ] column.

Hi Marco,

Just to confirm: do you see this at ROOT startup? And for which root version? Linux?

Cheers,
D

The issue happens when I load the libXXX library and start using templated class MyClass. I use ROOT 6.32 and 6.34, issue tested on macOS and Linux on my side.

I believe this issue has also been reproduced by @vpadulan earlier this year.

Hi @Danilo

Would you have any further idea how to fix that ?
Or some possible investigation I could do ? I basically don’t know where this error comes from and don’t know where to look at.

Best regards,
M

Support for enum class as template arguments in a I/O class (i.e your case) using rootmap files is not yet implemented (side note, support for unscoped enum as template arguments in an I/O class using rootmap files can not be supported as they can not be forward declared).

The file interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp would need to be enhance to print the necessary enum class declarations.

Alternatively you can try to switch to the newer ‘cxx module’ based dictionary (replacing both the rootmap file and headers), using something like:

rootcling ....  -s libLibraryName.so -cxxmodule -m DepsLibrary1_xr_rdict.pcm -m DepsLibrary2_xr_rdict.pcm -moduleMapFile=<path/module.modulemap> -moduleMapFile=<path/module.modulemap 

(see Modules — Clang 20.0.0git documentation)
or if you use the ROOT CMake utilities, just add the MODULE part:

ROOT_GENERATE_DICTIONARY(myapp_dict myapp.h MODULE myapp LINKDEF LinkDef.h)

Indeed, I use enum class, I believe it has some advantages as compared to enum.
I had a look at your file. It seems the method is partially implemented already:

# interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp

// [..]

if (D->isScopedUsingClassTag())
        Out() << "class ";
      else
        Out() << "struct ";

// [..]

I am already using ROOT_GENERATE_DICTIONARY with the MODULE option since couple of years so I believe I may use already this cxx module features under the hood.

ROOT_GENERATE_DICTIONARY(${DICTIONARY} ${HEADERS} MODULE ${LIBRARY} LINKDEF ${CMAKE_CURRENT_SOURCE_DIR}/include/${LINKDEF})

I may use already this cxx module features under the hood.

I would expect to but the fact that you have a rootmap file says no.

Which version of ROOT are you using? What does root-config --features prints?

Building my library, I get 4 files.

$ ls
libMyLib.dylib     libMyLib.rootmap   libMyLib_rdict.pcm    MyLib.Dict.cxx

I just removed libMyLib.rootmap and kept cxx & pcm files. I believe the cxx module based dictionary should be libMyLib_rdict.pcm ? Not sure why rootmap is prioritized, but the error message disappear if I just remove rootmap file. How to check everything is working ?

About other questions, here are the outputs

$ root-config --version
6.32.04
$ root-config --features
cxx17 asimage builtin_afterimage builtin_clang builtin_cling builtin_freetype builtin_ftgl builtin_gl2ps builtin_glew builtin_llvm builtin_nlohmannjson builtin_openui5 builtin_tbb builtin_vdt builtin_xxhash clad cocoa dataframe davix gdml http imt libcxx mathmore opengl pyroot roofit webgui root7 rpath runtime_cxxmodules shared sqlite ssl tmva tmva-cpu tmva-pymva spectrum vdt xml xrootd

No. It would be MyLib.pcm. The .rootmap and _rdict.pcm files are working together.

So something is going wrong(ish?) in the cmake execution (and/or my memory of how it should work) of ROOT_GENERATE_DICTIONARY … since you have ROOT configured with runtime_cxxmodules. I need to try to reproduce this. Can you give me access to your code/project?

1 Like

Sure ok, I don’t see any MyLib.pcm. Please find attached the following 3 files as example.

mkdir build && cd build
cmake ..
make -j

CMakeLists.txt (611 Bytes)
LinkDef.h (226 Bytes)
myheader.hxx (158 Bytes)

While preparing this example, I ended up moving my hand the outgoing files one by one: libMyLib.dylib, libMyLib_rdict.pcm, libMyLib.rootmap.

Here are some of my observations. I would be

mkdir lib && cd lib

mv ../libMyLib.dylib .
# root [0] .L libMyLib.dylib
# Error in <TCling::LoadPCM>: ROOT PCM ./pcanal/build/lib/libMyLib_rdict.pcm file does not exist
# Info in <TCling::LoadPCM>: In-memory ROOT PCM candidate ./root/20240914/lib/libASImageGui_rdict.pcm
# [...]

mv ../libMyLib_rdict.pcm libMyLib_rdict.pcm
# root [0] .L libMyLib.dylib
# <no error>, can use the library and execute my templated class

mv ../libMyLib.rootmap libMyLib.rootmap
#$ root -l 
#Forward declarations from ./libMyLib.rootmap:2:11: error: unknown type name 'MyEnum'
#template <MyEnum> class MyClass;
#          ^
#Warning in <TInterpreter::LoadLibraryMap>: Problems in (null) declaring '
#line 2 "Forward declarations from [...]

To enable the use the module, add:

-Druntime_cxxmodules=ON

to your project’s cmake invocation.

When using the older alternative, the file libMyLib_rdict.pcm is indeed necessary while the file libMyLib.rootmap is optional but enables the autoloading of libraries.

I managed to run cxxmodules and also generate a module.modulemap. It can be renamed at installation stage and then I just point to it using ‘CLING_MODULEMAP_FILES’ to load it with a custom name and location.

I don’t get anymore the enum class issue using this new method. I just noticed my new .pcm is about 130MB. I believe this is due to the embedded libraries implemented in the source.