Linking problems with shared library _BACKWARD_BACKWARD_WARNING_H

Hi,

I’m trying to use a shared library containing classes with inheritance. Unfortunately, I am running into some compatibility problems and in the current state can’t use the library both with the interpreter and the compiler at the same time. Help would be appreciated.

For example, I have class A and a shared library A.sl as well as a class B which inherits from class A:

#ifndef _B
#define _B
#include "A.h"

class B : public A {
public:
	B(int arg) : A(arg) {}
	void funcB();
};
#endif

Now, when I compile this macro:

#include "A.h"
#include "B.h"
void macro() {
	A a(0);
	a.funcA();
	B b(0);
	b.funcA();
}

Everything works nicely.
However, when I load the library with gSystem->Load("A.sl"); and then execute the macro without compiling it I get the errors:

error: base class has incomplete type
class B : public A {
          ~~~~~~~^
A.h:5:7: note: definition of 'A' is not complete until the closing '}'
class A {
      ^
In file included from ADictUX dictionary payload:6:
B.h:7:15: error: type 'A' is not a direct or virtual base of 'B'
        B(int arg) : A(arg) {}
                     ^
Error in <TInterpreter::AutoParse>: Error parsing payload code for class A with content:

#line 1 "ADictUX dictionary payload"


#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
#include "A.h"
#include "B.h"

#undef  _BACKWARD_BACKWARD_WARNING_H

Error in <TInterpreter::AutoParse>: Error parsing payload code for class B with content:

#line 1 "ADictUX dictionary payload"

#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
#include "A.h"
#include "B.h"

#undef  _BACKWARD_BACKWARD_WARNING_H

Now, I can avoid the these errors by including only
#pragma link off all classes; in the LinkDef.h. Unfortunately, this is not really an option since it makes the library unusable for example with ROOT.gSystem.Load('A.sl').

Is there a way to make all three options work at the same time? If needed I can also attach the full example.

Can you share your A.h file as well as how you generate the library?

Of course, A.h contains

#ifndef _A
#define _A
#include <iostream>

class A {
public:
	A(int arg) {
		std::cout << "init A with arg=" << arg << std::endl;
	}
	void funcA();
};
#endif

And the library is generated with a Makefile containing

LD              =g++

CXXFLAGS        =-fPIC -g -O -Wall -Wextra
INCROOT         =$(shell root-config --incdir)
LIBSLIN			=$(shell root-config --glibs)
CPPFLAGS        =-I $(INCROOT)/ -I $(INCDIR)/

DICTB           =ADictUX
DICTH           =${DICTB}.h
DICT            =${DICTB}.cc
DICTO           =${DICTB}.o

HDRS            =A.h B.h LinkDef.h
DICTHDRS        =$(HDRS) 
OBJS            =A.o B.o $(DICTO)
SLL             =A.sl

#__________________________________________________________

all:		${OBJS} ${DICTO}
		${LD} -shared ${CXXFLAGS} -o ${SLL} ${OBJS} ${LIBSLIN}	
		
${DICT}:        ${DICTHDRS}
		rootcint -f ${DICT} -c ${DICTHDRS}
		
%.o             : %.cc
		${LD} ${CPPFLAGS} ${CXXFLAGS} -c $< -o $@

I have also attached the full example with a C++ and a Python macro.
linker_example.zip (1.8 KB)

I can reproduce the problem … You can work around it by using macro.cc:

//#include "A.h"
#include "B.h"

void macro() {
        A a(0);
        a.funcA();
        B b(0);
        b.funcA();
}

Thank you for having a look! Unfortunately, the proposed workaround does not really help since it would lead to user error and it does not fix the underlying problem. For example, this macro.cc still fails:

#include "A.h"
//#include "B.h"

void macro() {
        A a(0);
        a.funcA();
        //B b(0);
        //b.funcA();
}

It seems the shared library does not catch the header guard in A.h when executed in Cling. For _B the header guard is caught by the shared library (try moving #include "A.h" outside of the header guard).

Any other ideas how to fix this or is it a bug in Cling?

I don’t know yet. I am still investigating.

1 Like

It seems like the error is not directly related to inheritance but it happens when a class is initialized in the constructor of another class. This will produce the same error:

#ifndef _B
#define _B
#include "A.h"

class B {
public:
	B(int arg) { 
		A a(arg); 
	}
	void funcB();
};
#endif

However, there is no error if A is initialized only in funcB().

Is there a way to define a child class that works with Cling?

Hi, I suppose Cling currently does not support C++11 inheritance of constructors. Will this be implemented in the future?

Cling does support inherited constructors. The issue is that the script triggers loading of a shared library in the middle of parsing, and recursively defines the same thing.

The script can use R__LOAD_LIBRARY(YourLibName) at the top of the script, to trigger loading before entering the recursive class definition.

Or you can outline the constructor - that should work, too.

Or you can generate a C++ dictionary module, see the command line flags for rootcling.

Is any of this an option in your use case?

Cheers, Axel

Dear Axel, thanks for the clarification!

I’m not quite sure how to implement the proposed workarounds and would be very glad if you could elaborate.

  • Adding R__LOAD_LIBRARY(YourLibName) to the macro does not change the behavior.

  • What do I need to change to outline the constructor?

#ifndef _A
#define _A
#include <iostream>
class A {
public:
	A(int);
};
#endif
  • Unfortunately, I could not find information on how to create and use a C++ dictionary module in a way that would solve this issue.

Here is a minimal example (where I tried to implement the proposals) to reproduce the error.
A.zip (1.2 KB)

Cheers,
Christian

Short answer yes, of course, we have many working examples. … Long answer … sorry for the delay I am investigating why this works in a lot of other cases but not your specific setup and testing the proper workaround for you.

Okay great, thanks a lot!

The full story can be read in the issue report: Bundling base class and derived class in same dictionary can lead to failure in recursive parsing. · Issue #13034 · root-project/root · GitHub

To work-around the issue you just have to generate a rootmap file (to be kept in the same directory as the library). In the following the name of the rootmap file is A.rootmap (to match with the library name).

diff Makefile.orig Makefile
25c25
<               rootcint -f ${DICT} -c ${DICTHDRS}
---
>               rootcint -rml=A -f ${DICT} -c ${DICTHDRS}

Great, this works smoothly!
Thank you for the help and for sorting this out!

Cheers
Christian

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.