Dictionary issue with consteval and spdlog/fmt

I have a class with following template member function:

template<typename T, typename Loc = std::initializer_list<size_t>>
auto get_slot(Loc loc) -> T*&
    requires types::LocatorContainer<Loc>
{
    const auto pos = header.loc2pos(loc);
    if (!header.set_map_index(pos, pos)) {
        spdlog::warn("Category {} was already compressed, can't add new slots.", header.name);
        throw std::runtime_error("Cannot access compressed category");
    }
    return reinterpret_cast<T*&>(data->operator[](types::size_t2int(pos)));
}

The class has generated dictionary with

// the header
ClassDefOverride(the_class, 1)
// Linkdef
#pragma link C++ class the_class+;

While opening up a ROOT file with stored object of this class in the TBrowser (I can do root file.root but the error shwos up when I clink on the file in the browser), I end up with following error:

path/to/header/file.hpp:204:26: error: call to consteval function 'fmt::fstring<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &>::fstring<57UL>' is not a constant expression
            spdlog::warn("Category {} was already compressed, can't add new slots.", header.name);
                         ^
note: cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression

The full crash stack is:

===========================================================
There was a crash.
This is the entire stack trace of all threads:
===========================================================
#0  0x00007fe19b916417 in ?? () from /usr/lib64/libc.so.6
#1  0x00007fe19b916441 in ?? () from /usr/lib64/libc.so.6
#2  0x00007fe19b98552b in wait4 () from /usr/lib64/libc.so.6
#3  0x00007fe19b8d6dfb in ?? () from /usr/lib64/libc.so.6
#4  0x00007fe19c0942c0 in TUnixSystem::StackTrace() () from /usr/lib64/root/libCore.so.6.34
#5  0x00007fe19c093b84 in TUnixSystem::DispatchSignals(ESignals) () from /usr/lib64/root/libCore.so.6.34
#6  <signal handler called>
#7  0x00007fe198718173 in ?? () from /usr/lib64/root/libCling.so
#8  0x00007fe195956800 in ?? () from /usr/lib64/root/libCling.so
#9  0x00007fe1959577ac in ?? () from /usr/lib64/root/libCling.so
#10 0x00007fe19595791b in ?? () from /usr/lib64/root/libCling.so
#11 0x00007fe195923c31 in ?? () from /usr/lib64/root/libCling.so
#12 0x00007fe1959240b6 in ?? () from /usr/lib64/root/libCling.so
#13 0x00007fe195906f96 in ?? () from /usr/lib64/root/libCling.so
#14 0x00007fe19598dc38 in ?? () from /usr/lib64/root/libCling.so
#15 0x00007fe195991007 in ?? () from /usr/lib64/root/libCling.so
#16 0x00007fe195907abe in ?? () from /usr/lib64/root/libCling.so
#17 0x00007fe195907c37 in ?? () from /usr/lib64/root/libCling.so
#18 0x00007fe19582b4f5 in ?? () from /usr/lib64/root/libCling.so
#19 0x00007fe19582d619 in TCling::AutoParseImplRecurse(char const*, bool) () from /usr/lib64/root/libCling.so
#20 0x00007fe1958343f0 in TCling::AutoParse(char const*) () from /usr/lib64/root/libCling.so
#21 0x00007fe19c022229 in TClass::LoadClassInfo() const () from /usr/lib64/root/libCore.so.6.34
#22 0x00007fe19c024968 in TClass::NewObject(TClass::ENewType, bool) const () from /usr/lib64/root/libCore.so.6.34
#23 0x00007fe19c028125 in TClass::New(TClass::ENewType, bool) const () from /usr/lib64/root/libCore.so.6.34
#24 0x00007fe19b2ed8db in TKey::IsFolder() const () from /usr/lib64/root/libRIO.so
#25 0x00007fe181353afc in TGFileBrowser::GetObjPicture(TGPicture const**, TObject*) () from /usr/lib64/root/libGui.so.6.34.08
#26 0x00007fe181353e30 in TGFileBrowser::AddKey(TGListTreeItem*, TObject*, char const*) () from /usr/lib64/root/libGui.so.6.34.08
#27 0x00007fe181354615 in TGFileBrowser::Add(TObject*, char const*, int) () from /usr/lib64/root/libGui.so.6.34.08
#28 0x00007fe19c013e0b in TBrowser::Add(TObject*, char const*, int) () from /usr/lib64/root/libCore.so.6.34
#29 0x00007fe19b2aea41 in TDirectoryFile::Browse(TBrowser*) () from /usr/lib64/root/libRIO.so
#30 0x00007fe1813552b3 in TGFileBrowser::DoubleClicked(TGListTreeItem*, int) () from /usr/lib64/root/libGui.so.6.34.08
#31 0x00007fe19588b226 in TClingCallFunc::exec(void*, void*) () from /usr/lib64/root/libCling.so
#32 0x00007fe19bf66698 in TQConnection::SendSignal() () from /usr/lib64/root/libCore.so.6.34
#33 0x00007fe18139fb2b in ?? () from /usr/lib64/root/libGui.so.6.34.08
#34 0x00007fe1813a3455 in TGListTree::HandleDoubleClick(Event_t*) () from /usr/lib64/root/libGui.so.6.34.08
#35 0x00007fe18137e0bd in TGFrame::HandleEvent(Event_t*) () from /usr/lib64/root/libGui.so.6.34.08
#36 0x00007fe18131db1d in TGClient::HandleEvent(Event_t*) () from /usr/lib64/root/libGui.so.6.34.08
#37 0x00007fe18131e005 in TGClient::ProcessOneEvent() () from /usr/lib64/root/libGui.so.6.34.08
#38 0x00007fe18131e05a in TGClient::HandleInput() () from /usr/lib64/root/libGui.so.6.34.08
#39 0x00007fe19c0933eb in TUnixSystem::DispatchOneEvent(bool) () from /usr/lib64/root/libCore.so.6.34
#40 0x00007fe19bf97259 in TSystem::Run() () from /usr/lib64/root/libCore.so.6.34
#41 0x00007fe19bf17927 in TApplication::Run(bool) () from /usr/lib64/root/libCore.so.6.34
#42 0x00007fe19c25142d in TRint::Run(bool) () from /usr/lib64/root/libRint.so.6.34
#43 0x0000562e4486f303 in main ()

Any idea how to deal with it?

Setup

ROOT v6.34.08
Built for linuxx8664gcc on Apr 22 2025, 15:05:04
From tags/6-34-08@6-34-08
With 
Binary directory: /usr/bin

Additional context

ROOT compiled with C++20 and my code with C++23.

Hi @rlalik ,

May be @pcanal can help with your problem.

This seems to be an issue with the interpretation of your header files. You may need to provide us with a complete reproducer to make progress on this issue.

@devajithvs any ideas?

Hi, I will ping this topic to avoid closing after 14 days, but my bad/good news is that while I was preparing the code to be committed to the repo for providing WE, I had to do something that this error was gone. I tried to bring it back but wasn’t succesful.
But now I have similar but different errors, let me try to demonstrate them soon.

Ok, this time I have following issue:

When opening the root file with tree T in the browser, when expanding the data member (which is of TClonesArray) inside the leaf (custom class derived from TObject with auto IsFolder() const -> Bool_t override { return kTRUE; }), as shown in the image:

the following error pop-ups (but only when double clicking on data member, not earlier):

root [2] libsabat dictionary payload:17:10: remark: could not acquire lock file for module 'sabat': failed to create unique file /usr/lib64/root/sabat.pcm.lock-cfd0d3ec: Permission denied [-Rmodule-build]
#include "/home/rafal/sabat/src/sabat-framework/include/sabat/sabat_categories.hpp"
         ^
libsabat dictionary payload:17:10: remark: building module 'sabat' as '/usr/lib64/root/sabat.pcm' [-Rmodule-build]
error: unable to open output file '/usr/lib64/root/sabat.pcm': 'Permission denied'
libsabat dictionary payload:17:10: remark: finished building module 'sabat' [-Rmodule-build]
libsabat dictionary payload:17:10: fatal error: could not build module 'sabat'
#include "/home/rafal/sabat/src/sabat-framework/include/sabat/sabat_categories.hpp"
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error in <TInterpreter::AutoParse>: Error parsing payload code for class SabatRaw with content:

#line 1 "libsabat dictionary payload"

#ifndef FMT_SHARED
  #define FMT_SHARED 1
#endif
#ifndef SPDLOG_SHARED_LIB
  #define SPDLOG_SHARED_LIB 1
#endif
#ifndef SPDLOG_COMPILED_LIB
  #define SPDLOG_COMPILED_LIB 1
#endif
#ifndef SPDLOG_FMT_EXTERNAL
  #define SPDLOG_FMT_EXTERNAL 1
#endif

#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
#include "/home/rafal/sabat/src/sabat-framework/include/sabat/sabat_categories.hpp"
#include "/home/rafal/sabat/src/sabat-framework/include/sabat/sabat_definitions.hpp"

#undef  _BACKWARD_BACKWARD_WARNING_H

Error in <TClass::LoadClassInfo>: no interpreter information for class SabatRaw is available even though it has a TClass initialization routine.

It tries t make pcm file inside /usr/lib64/root/ which is my ROOT lib’s installation path.
However I have in my current directory:

-rw-r--r-- 1 rafal rafal    2887 06-05 20:32 libsabat_rdict.pcm
-rw-r--r-- 1 rafal rafal     567 06-05 20:32 libsabat.rootmap
lrwxrwxrwx 1 rafal rafal      13 06-06 12:23 libsabat.so -> libsabat.so.0
lrwxrwxrwx 1 rafal rafal      17 06-06 12:23 libsabat.so.0 -> libsabat.so.0.1.0
-rwxr-xr-x 1 rafal rafal 2245280 06-06 12:23 libsabat.so.0.1.0

and

$ echo $LD_LIBRARY_PATH
/home/rafal/sabat/src/sabat-framework/build/default:/tmp/spark/lib64/:/home/rafal/usr/lib64:/home/rafal/.local/lib64
$ pwd
/home/rafal/sabat/src/sabat-framework/build/default

which makes rootlogon script loading the proper dictionaries. This is the error I met for the second time, and have no idea how to handle it.

Is it connected with the features the root is compiled:

$ root-config --features
cxx23 asimage asimage_tiff builtin_clang builtin_cling builtin_llvm builtin_openui5 dataframe davix fcgi fftw3 gdml geom gnuinstall gviz http imt mathmore mpi opengl pyroot qt6web roofit root7 runtime_cxxmodules shared soversion spectrum ssl tmva tmva-cpu tmva-cudnn tmva-pymva tpython unuran use_gsl_cblas webgui x11 xml

@pcanal @couet @devajith any ideas how to approach this problem? Forgot to mention that this is root 6.36.00 compiled with c++23.

root [2] libsabat dictionary payload:17:10: remark: could not acquire lock file for module 'sabat': 
     failed to create unique file /usr/lib64/root/sabat.pcm.lock-cfd0d3ec: 
         Permission denied [-Rmodule-build]
#include "/home/rafal/sabat/src/sabat-framework/include/sabat/sabat_categories.hpp"
         ^
libsabat dictionary payload:17:10: remark: building module 'sabat' as '/usr/lib64/root/sabat.pcm' [-Rmodule-build]
error: unable to open output file '/usr/lib64/root/sabat.pcm': 'Permission denied'

I have not seen this error before (@vvassilev Did you see this one before?).

Do you have write access to the directory /usr/lib64/root ?

Thanks,
Philippe.

No, obviously this is the system location and as a user I should now have an access there.

The problem above is present with root-6.36.00 compiled on gentoo with following options:

cmake -C /var/tmp/portage/sci-physics/root-6.36.00/work/root-6.36.00_build/gentoo_common_config.cmake
-G Unix Makefiles -DCMAKE_INSTALL_PREFIX=/usr
-DCMAKE_C_COMPILER=x86_64-pc-linux-gnu-gcc
-DCMAKE_CXX_COMPILER=x86_64-pc-linux-gnu-g++
-DCMAKE_CUDA_HOST_COMPILER=x86_64-pc-linux-gnu-g++
-DCMAKE_C_FLAGS=-march=skylake -O2 -pipe
-DCMAKE_CXX_FLAGS=-march=skylake -O2 -pipe
-UCMAKE_C_FLAGS_RELEASE
-UCMAKE_C_FLAGS_RELWITHDEBINFO
-UCMAKE_CXX_FLAGS_RELEASE
-UCMAKE_CXX_FLAGS_RELWITHDEBINFO
-DLLVM_BUILD_TYPE=Release
-DPYTHON_EXECUTABLE=/usr/bin/python3.13
-DDEFAULT_SYSROOT= -DCMAKE_INSTALL_PREFIX=/usr
-DCMAKE_INSTALL_CMAKEDIR=lib64/cmake/ROOT
-DCMAKE_INSTALL_DATADIR=share/root
-DCMAKE_INSTALL_DOCDIR=share/doc/root-6.36.00
-DCMAKE_INSTALL_FONTDIR=share/fonts/root
-DCMAKE_INSTALL_INCLUDEDIR=include/root
-DCMAKE_INSTALL_LIBDIR=lib64/root
-DCMAKE_INSTALL_PYTHONDIR=/usr/lib/python3.13/site-packages
-DCMAKE_INSTALL_SRCDIR=/usr/src/debug/sci-physics/root-6.36.00
-DCMAKE_INSTALL_SYSCONFDIR=share/root
-DCMAKE_INSTALL_TUTDIR=share/root/tutorials
-DCLING_BUILD_PLUGINS=OFF
-Dasan=OFF -Dasserts=no
-Dccache=OFF -Dcoverage=OFF
-Ddev=OFF -Ddistcc=OFF
-Dfail-on-missing=ON
-Dgnuinstall=ON -Dgminimal=OFF
-Dshared=ON -Dsoversion=ON
-Dbuiltin_llvm=ON -Dbuiltin_clang=ON -Dbuiltin_cling=ON -Dbuiltin_openui5=ON
-Dbuiltin_cfitsio=OFF -Dbuiltin_cppzmq=OFF -Dbuiltin_davix=OFF -Dbuiltin_fftw3=OFF
-Dbuiltin_freetype=OFF -Dbuiltin_ftgl=OFF -Dbuiltin_gl2ps=OFF -Dbuiltin_glew=OFF
-Dbuiltin_gsl=OFF -Dbuiltin_gtest=OFF -Dbuiltin_lz4=OFF -Dbuiltin_lzma=OFF
-Dbuiltin_nlohmannjson=OFF -Dbuiltin_openssl=OFF -Dbuiltin_pcre=OFF -Dbuiltin_tbb=OFF
-Dbuiltin_unuran=OFF -Dbuiltin_vc=OFF -Dbuiltin_vdt=OFF -Dbuiltin_veccore=OFF
-Dbuiltin_xrootd=OFF -Dbuiltin_xxhash=OFF -Dbuiltin_zeromq=OFF -Dbuiltin_zlib=OFF
-Dbuiltin_zstd=OFF -Darrow=OFF -Dasimage=yes -Dcefweb=OFF -Dclad=OFF -Dcocoa=no
-Dcuda=no -Dcudnn=no -Dcxxmodules=OFF -Ddaos=OFF -Ddataframe=ON -Ddavix=no
-Ddcache=OFF -Dfcgi=yes -Dfftw3=no -Dfitsio=no -Dfortran=no -Dgdml=no -Dgviz=yes
-Dhttp=yes -Dimt=no -Dlibcxx=no -Dmathmore=yes -Dminuit=yes -Dmlp=yes -Dmpi=no
-Dmysql=no -Dodbc=no -Dopengl=yes -Dpgsql=no -Dpyroot=yes -Dpythia8=no -Dqt5web=OFF
-Dqt6web=yes -Dr=no -Droofit=yes -Droofit_multiprocess=OFF -Droofit_hs3_ryml=OFF
-Droot7=yes -Drootbench=OFF -Droottest=OFF -Drpath=OFF -Druntime_cxxmodules=ON
-Dshadowpw=no -Dspectrum=ON -Dsqlite=no -Dssl=no -Dtest_distrdf_dask=OFF
-Dtest_distrdf_pyspark=OFF -Dtesting=no -Dtmva=yes -Dtmva-cpu=yes -Dtmva-gpu=no
-Dtmva-pymva=yes -Dtmva-rmva=no -Dtmva-sofie=OFF -Dunuran=no -During=no -Dvc=no
-Dvdt=OFF -Dveccore=OFF -Dvecgeom=OFF -Dwebgui=yes -Dx11=yes -Dxml=no -Dxrootd=no
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DCMAKE_TOOLCHAIN_FILE=/var/tmp/portage/sci-physics/root-6.36.00/work/root-6.36.00_build/gentoo_toolchain.cmake
-DCMAKE_CXX_STANDARD=23 /var/tmp/portage/sci-physics/root-6.36.00/work/root-6.36.00

I have also separate installation of root from git:master, where my code works:

cmake -S root -B build
-DCMAKE_CXX_STANDARD=23 -DDEFAULT_SYSROOT=
-DCLING_BUILD_PLUGINS=OFF -DCMAKE_INSTALL_PREFIX=${IDIR}
-Dfail-on-missing=ON -Dgnuinstall=OFF -Dshared=ON -Dsoversion=ON
-Dbuiltin_llvm=ON -Dbuiltin_clang=ON -Dbuiltin_afterimage=OFF -Dbuiltin_cfitsio=OFF
-Dbuiltin_davix=OFF -Dbuiltin_fftw3=OFF -Dbuiltin_freetype=OFF -Dbuiltin_ftgl=OFF
-Dbuiltin_gl2ps=OFF -Dbuiltin_glew=OFF -Dbuiltin_gsl=OFF -Dbuiltin_lz4=OFF
-Dbuiltin_lzma=OFF -Dbuiltin_nlohmannjson=ON -Dbuiltin_openssl=OFF
-Dbuiltin_pcre=OFF -Dbuiltin_tbb=off -Dbuiltin_unuran=OFF -Dbuiltin_vc=OFF
-Dbuiltin_vdt=OFF -Dbuiltin_veccore=OFF -Dbuiltin_xrootd=OFF -Dbuiltin_xxhash=OFF
-Dbuiltin_zlib=OFF -Dbuiltin_zstd=OFF -Dalien=OFF -Darrow=OFF -Dasimage=ON
-Dbonjour=off -Dcastor=off -Dccache=OFF -Dcefweb=OFF -Dclad=OFF -Dcocoa=OFF
-Dcuda=OFF -Dcudnn=OFF -Dcxxmodules=OFF -Ddataframe=ON -Ddavix=OFF
-Ddcache=OFF -Dfcgi=OFF -Dfftw3=ON -Dfitsio=OFF -Dfortran=ON -Dgdml=ON
-Dgfal=OFF -Dgminimal=OFF -Dgviz=OFF -Dhttp=ON -Dimt=OFF -Dlibcxx=OFF
-Dmathmore=ON -Dmemstat=OFF -Dminimal=OFF -Dminuit=ON -Dmlp=ON
-Dmonalisa=OFF -Dmpi=OFF -Dmysql=OFF -Dodbc=OFF -Dopengl=ON -Doracle=OFF
-Dpgsql=OFF -Dpyroot=ON -Dpyroot_legacy=OFF -Dpythia6=OFF -Dpythia8=OFF
-Dqt5web=OFF -Dr=OFF -Droofit=ON -Droot7=ON -Drootbench=OFF -Droottest=OFF
-Drpath=OFF -Druntime_cxxmodules=OFF -Dshadowpw=OFF -Dspectrum=ON
-Dsqlite=OFF -Dssl=OFF -Dtcmalloc=OFF -Dtesting=OFF -Dtmva=ON -Dtmva-cpu=ON
-Dtmva-gpu=OFF -Dtmva-pymva=ON -Dtmva-rmva=OFF -Dunuran=OFF -Dvc=OFF
-Dvdt=OFF -Dveccore=OFF -Dvecgeom=OFF -Dvmc=OFF -Dx11=ON -Dxml=ON
-Dxrootd=OFF -Drfio=OFF -Dldap=OFF -Dchirp=OFF -Dhdfs=OFF

I suspect that the value of -Druntime_cxxmodules= may matter as the gentoo install has it set to ON (this is not working) whereas my local has set to OFF (this works).

I also prepared a MWE demonstrating the problems: GitHub - rlalik/root_issues_demo: Repository to demonstrate some ROOT issues with modern C++

There is matrix of four cases:

  1. runtime_cxxmodules off; macro interpreted,
  2. runtime_cxxmodules off; macro compiled (ACLiC),
  3. runtime_cxxmodules on; macro interpreted - this is interesting
  4. runtime_cxxmodules off; macro compiled (ACLiC) - only this works fine

Case 1. and 2. give similar result of root trying to create modules in the root (system wide in my case) lib dir. It is the same case as reported above.

Case 3. is another interesting case. When using #pragma once in the header (with guardian defines it works fine), the root is loading same header mutliple times (I guess once from macro and second from rdict?) and reports multiple definitions in same header:

Processing ../macro.C...
In file included from input_line_11:1:
In file included from /home/rafal/projects/cern/demos/cpp_23/macro.C:1:
/home/rafal/projects/cern/demos/cpp_23/include/header.hpp:7:8: error: redefinition of 'struct_foo'
struct struct_foo {};
       ^
input_line_12:1:10: note: '/home/rafal/projects/cern/demos/cpp_23/include/header.hpp' included multiple times, additional include site here
#include "/home/rafal/projects/cern/demos/cpp_23/include/header.hpp"
         ^
/home/rafal/projects/cern/demos/cpp_23/macro.C:1:10: note: '/home/rafal/projects/cern/demos/cpp_23/include/header.hpp' included multiple times, additional include site here
#include <header.hpp>
         ^
In file included from input_line_11:1:
In file included from /home/rafal/projects/cern/demos/cpp_23/macro.C:1:
/home/rafal/projects/cern/demos/cpp_23/include/header.hpp:9:13: error: redefinition of 'foo'
inline void foo() {}
            ^
/home/rafal/projects/cern/demos/cpp_23/include/header.hpp:9:13: note: previous definition is here
inline void foo() {}

This problem does not occur in case 4. which compiles fine and compiled macro executes properly. So there is an issue how the #pragma once is handled in rootcling I guess. I know that #pragma once is non standard but some libs are using it and soon or later root will need to deal with them as well I think.

I can open root issues on github if you wish for each of these cases.

Do you have a module.modulemap file under that folder or the sabat subfolder?

There is a file like that in the build directory (I guess it is created automatically).

So I assume it should be installed together with other files? Where should it go? Into include or lib in the installation?

This is perhaps hint that things like that should be better documented. There is a page: Integrating ROOT into CMake projects - ROOT which describes cmake integration only until library building, but not installation. Unless this is documented somewhere else.

I am also wondering about name collision. Since the name module.modulemap is generic, what if I want to install a couple of projects into same path? Could it be called like my_project.modulemap, same like names for rootmap or rdict are generated?

Who generated that modulemap file?

Not me, so I believed it was generated by root? In my demonstrator: root_issues_demo/CMakeLists.txt at 893eab8f71ca803cdc94ce5103200966c6fd72bb · rlalik/root_issues_demo · GitHub it is also generated.

Ah, okay, now I can follow. So we build a dictionary which has a dependency to a third party library and we try to build a c++/clang module for it. All of this makes sense the question is why we try to build the module in the system folder and not in $ROOTSYS/lib or the library folder of your project…

It is a bit simpler. I just build my own library without external dependencies, but when I try to use some macro with this library, then the module is tried to be created (this is what is done in my simple demonstrator). The system location is kind of $ROOTSYS/lib, but there is actually no $ROOTSYS used at all (see the cmake options in some post above for the gentoo installation).