TRint app using custom namespace

I’m trying to create a C++ interpreter app using CINT that would include a header only toolbox namespace. I was able to build a mockup with cmake using:

gundamRoot (root-like app front end which includes my toolbox lib)

#include "TROOT.h"
#include "TRint.h"

#include <cstdlib>

TROOT root("Rint","The ROOT Interactive Interface");

int main(int argc, char **argv) {
  auto theApp = TRint("Rint", &argc, argv, nullptr, 0);
  theApp.ProcessLine("#include \"GenericToolbox.h\"");
  theApp.Run();
  return EXIT_SUCCESS;
}

LinkDef.h file:

#ifdef __CINT__

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

#pragma link C++ defined_in "GenericToolbox.h";

#endif

And the CMakeList.txt:

ROOT_GENERATE_DICTIONARY(
        G__GundamRootDict
        ${CMAKE_SOURCE_DIR}/submodules/cpp-generic-toolbox/include/GenericToolbox.h
        LINKDEF
        ${CMAKE_CURRENT_SOURCE_DIR}/LinkDef.h
)

add_library(GundamRootDict SHARED G__GundamRootDict.cxx)
target_link_libraries(GundamRootDict ${ROOT_LIBRARIES})

#---Create  a main program using the library
add_executable( gundamRoot src/gundamRoot.cxx )
target_link_libraries( gundamRoot GundamRootDict )

This implementation works well although it actually needs to recompile the header lib each time we run gundamRoot (cf. line theApp.ProcessLine("#include \"GenericToolbox.h\"");).

Is there a way this line could be compiled while building gundamRoot?

Cheers,
Nadrino


ROOT Version: 6.26.06
Platform: macOS
Compiler: clang


I’m sot sure to understand what you’re asking for. theApp.ProcessLine("#include \"GenericToolbox.h\""); doesn’t compile anything, it just includes the header. What do you want to compile exactly?

I would like to include this header library, I mean being able to access the namespace GenericToolbox:: without having to explicitly call "#include \"GenericToolbox.h\"" in runtime or with ProcessLine.

I don’t know have Rint does while parsing this instruction, but it takes few extra seconds while running gundamRoot

Well, then maybe @axel can help you with this

1 Like

I guess it would help if you provided a standalone reproducer.

Well here is the full cmake file: gundam/CMakeLists.txt at feature/banffFit · gundam-organization/gundam · GitHub

It should be not too complicated as for the moment no dependencies from the whole software is included

What are you then planning to do with GenericToolbox.h?

Unless you’re storing data in ROOT files you shouldn’t need any of

  • LinkDef.h
  • dictionary (the ROOT_GENERATE_DICTIONARY invocation isn’t needed)

I see :thinking:

I would like to get access to every function defined in this file. It’s just a namespace of functions with few classes. Is it mandatory to call #include each time I start the interpreter?

It’s for me the first iteration of include my libraries defined in the project for the interpreter

If you want to call these functions through the interpreter then yes, #include-ing that header would be a straight-forward solution (no need for anything else).

But that begs the question: why do you want to call things through the interpreter? (Sorry for being so curious, in principle there are only very few reasons to use the interpreter at all, and I’d like to understand which one is yours, or whether we can find a better solution.)

Indeed, but the drawback is also that I also need to keep the header files in the install directory to make ROOT know where it has to get the sources.

Yeah, it’s not a very common usage I guess, but the idea is to get an interpreter that is capable of runing macros which use simple handy functions defined in my GenericToolbox.h. In the future I’d also would like to include classes for the software library itself.

In fact the library I created is a generic fitter called GUNDAM which is designed to host various neutrino analysis (X-section measurement or oscillation analysis). It’s composed of a core engine which able to propagate parameter on samples. All of it is configurable with a buch of YAML/JSON config files, and so no analysis-dependant pieces of codes should be included in the library. However some people would like to use the interpreter to extract data from the output files or even perform side analysis with the core engine of GUNDAM, and having the tools available in the interpreter is a nice feature to have :grinning: (especially if people want to share their work/inputs).

In other words, “GenericToolbox” add-ons should automatically be visible in the interpreter, like any other ROOT classes (no need to “#include” anything).
I would assume that building one’s own “root.exe” main, linked with the “GenericToolbox” dictionary, was sufficient.

Exactly, it would save time while launching the gundamRoot exe and it would not rely on the presence of the header file GenericToolbox.h on the computer.

Yeah, that’s what I would have assumed too. Although it is a header-only library so I’m not sure it works the same way!

I barely remember a similar problem from a long long time ago (in ROOT 5).
The line:
#pragma link C++ defined_in "include.h";
wasnt’ simply generating any dictionary entries (if I remember well, it worked only if I used the “/full/path/to/my/include.h” file there).

1 Like

Oh indeed, it was the issue!

By looking into the generated .cxx file, the // Header files passed as explicit arguments automatically finds the right path, although the static const char* classesHeaders[] = { is not filled up when the full path is not provided in the LinkDef.h file.

Is this a bug? → Edit: in fact the // Header files passed as explicit arguments is filled up with the cmake includes ROOT_GENERATE_DICTIONARY. But having the explicit include file doesn’t make LinkDef.h aware of what to unfold

Also, is there a way to pass preprocessor definitions to ROOT_GENERATE_DICTIONARY so we can use the full path in the LinkDef.h?

Alright so after a few tries, it seems to be loading the header library on runtime without having to explicitly call #include. However the function are only available once I defined a dummy class. Typically here is what happens:

The source files I have are now:

gundamRoot.cxx:

#include "TRint.h"

#include <cstdlib>

int main(int argc, char **argv) {
  auto theApp = TRint("Rint", &argc, argv, nullptr, 0);
  theApp.Run();
  return EXIT_SUCCESS;
}

LinkDef.in.h (which is provided to CMake to auto generate the file with the full path)

#ifdef __CINT__

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

#pragma link C++ defined_in "@CMAKE_SOURCE_DIR@/submodules/cpp-generic-toolbox/include/GenericToolbox.h";
#pragma link C++ defined_in "@CMAKE_SOURCE_DIR@/submodules/cpp-generic-toolbox/include/GenericToolbox.TablePrinter.h";

#endif

And the CMakeLists.txt:

# Need a full unfold of the source path
configure_file(
        ${CMAKE_CURRENT_SOURCE_DIR}/LinkDef.in.h
        ${CMAKE_CURRENT_BINARY_DIR}/LinkDef.h
)

# Generate .cxx file
# Explicit include of the GenericToolbox.Root.h
ROOT_GENERATE_DICTIONARY(
        G__GundamRootDict
        ${CMAKE_SOURCE_DIR}/submodules/cpp-generic-toolbox/include/GenericToolbox.h
        ${CMAKE_SOURCE_DIR}/submodules/cpp-generic-toolbox/include/GenericToolbox.TablePrinter.h
        LINKDEF
        ${CMAKE_CURRENT_BINARY_DIR}/LinkDef.h
)

# Needs to be a shared library
add_library( GundamRootDict SHARED G__GundamRootDict.cxx )
target_link_libraries( GundamRootDict ${ROOT_LIBRARIES} )
install(TARGETS GundamRootDict DESTINATION lib)

# Also need to install dict files
install(
    FILES
        ${CMAKE_CURRENT_BINARY_DIR}/libGundamRootDict_rdict.pcm
        ${CMAKE_CURRENT_BINARY_DIR}/libGundamRootDict.rootmap
    DESTINATION
        ${CMAKE_INSTALL_PREFIX}/lib
    COMPONENT
        libraries
)

add_executable( gundamRoot src/gundamRoot.cxx )
target_link_libraries( gundamRoot GundamRootDict )

Is there a way I can make the interpreter recognize the functions from GenericToolbox:: without making this dummy call?

Several possible solutions (one is sufficient) …

  • instead of creating a “shared library” with the dictionary, directly link its “object file” into your new “root.exe”,
  • in your new “main” routine, call something “dummy” from your “GenericToolbox” (so that it will actually need the “shared library” with the dictionary when linking your new “root.exe”),
  • add “-Wl,--no-as-needed” to your linker flags before the “shared library” with the dictionary and “-Wl,--as-needed” after it (when linking your new “root.exe”).

Will this work for header only functions?

I tried this option but it didn’t work, same behavior as the previous message. Is this working for header only library?

Adding the following to the gundamRoot seem to work, but it is wierd have that extra call before the interpreter :sweat_smile:

theApp->ProcessLine("{ GenericToolbox::TablePrinter t; }");

It seems like --no-as-needed option is not available on macOS

For the first and the third problems, I think @Axel would need to say something (in particular, for the first problem, why “linking in” the object files with the dictionaries into the executable does not automatically make them visible in its “built-in” interpreter).

For the second problem … I always try to find something in the add-ons that “returns” something “trivial”, and then I use:
{{ type_of_trivial v = get_this_trivial(); v = v; }}
Often, my “trivial” is some “const char *”, and I usually have:
{{ const char *c = SomeClass::Class_Name(); c = c; }}

In your case, I can see in the “GenericToolbox.h” something that could easily be used:
{{ int v = GenericToolbox::Parameters::_verboseLevel_; v = v; }}

BTW. I usually use (from the good old times):

#pragma link C++ nestedclass;
#pragma link C++ nestedtypedef;

We only parse headers of dictionaries once we “need” them, and we remember only classes vs header, not function vs header. That’s why your object declaration makes the functions available.

You can add #pragma link C++ class GenericToolbox - does that help?

So, what is this supposed to do:

#pragma link C++ defined_in "include.h";

Will it create dictionary entries for everything that is “defined in” the file, or will it disregard the user’s request?

And why does it seem to neglect any “-I/full/path/to” and require an explicit “/full/path/to/include.h”?