Library Developers Battling Cyclic Header Dependency

In the boost library system certain libraries need to be compiled and linked into a project. For example, libraries would need to be compiled to remove cyclic header dependencies. In the boost library system I coundn't find any .cpp files that would indecate a compilable library. So how do library developers create compilable files in the boost libraries? The following two files would need to be broken up and compiled to remove the dependencies, correct? Ryan //File A.hpp class B; class A { double x; public: B convertToB(void) { return B(x); } }; //File B.hpp class A; class B { double y; public: A convertToA(void) { return A(y); } };

From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Ryan In the boost library system certain libraries need to be compiled and linked into a project. For example, libraries would need to be compiled to remove cyclic header dependencies. In the boost library system I coundn't find any .cpp files that would indecate a compilable library. So how do library developers create compilable files in the boost libraries? The following two files would need to be broken up and compiled to remove the dependencies, correct?
//File A.hpp class B;
class A { double x; public: B convertToB(void) { return B(x); } };
//File B.hpp class A;
class B { double y; public: A convertToA(void) { return A(y); } };
Not exactly, you have a syntax error because B() is not declared at the time you use it in A.hpp, assuming A.hpp is included first, otherwise it is A() that is not declared when used in B.hpp. I'm guessing this is the problem you are asking about? No boost library should have syntax errors of this type. If you rewrite it as follows it should compile as long as you include A.hpp first then B.hpp. You also need to declare the functions inline to prevent multiple definition link errors since the classes and their member functions are not templated. //File A.hpp class B; class A { double x; public: B inline convertToB(void); }; //File B.hpp class A; class B { double y; public: A inline convertToA(void) { return A(y); } }; B A::convertToB(void) { return B(x); } This type of cyclical dependency can happen, and I have several such in my own library, but should not cause a problem in compilation and build in general. There are more rules about needing to define functions before using them and when you introduce templates you get still more rules about functions need to be defined at the time a template that uses them is instantiated, but boost library authors will know and follow all of these rules. Regards, Luke

On Wed, Mar 16, 2011 at 2:18 PM, Simonson, Lucanus J < lucanus.j.simonson@intel.com> wrote:
In the boost library system I coundn't find any .cpp files that would indecate a compilable library.
Any idea where the compilable files are in the boost library?
assuming A.hpp is included first,
Is there a way to make the classes able to not care about which class is included first?
You also need to declare the functions inline to prevent multiple definition link errors since the classes and their member functions are not templated.
If the entire class is in a header doesn't this removed the need for the inline call? Ryan

From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Ryan
On Wed, Mar 16, 2011 at 2:18 PM, Simonson, Lucanus J
Any idea where the compilable files are in the boost library?
All the code compiles. If you mean cpp files, then it is true, many libraries are header file only and don't need to be compiled seperate from the project code that uses them. Just include the headers to use them. > assuming A.hpp is included first,
Is there a way to make the classes able to not care about which class is included first?
//File A.hpp class B; class A { double x; public: B inline convertToB(void); }; //File B.hpp class A; class B { double y; public: A inline convertToA(void); } }; //File AB_details.hpp B A::convertToB(void) { return B(x); } A B::convertToA(void) { return A(y); } A.hpp and B.hpp can be included in any order, but AB_details needs to be included after both and before convertToA() or convertToB() is used. > >You also need to declare the functions inline to prevent multiple definition link errors since the classes and their member functions are not templated.
If the entire class is in a header doesn't this removed the need for the inline call?
No, inline keyword is required for the function to be made a weak symbol in the object file. Definition in a header file does not by itself make a weak symbol. To the compiler and linker code is code regardless of whether it is in a file with .hpp or .cpp. We also have the extern keyword to provide information to the linker about what our intentions are. Regards, Luke

On Wed, Mar 16, 2011 at 3:16 PM, Simonson, Lucanus J < lucanus.j.simonson@intel.com> wrote:
In the boost library system I coundn't find any .cpp files that would indecate a compilable library.
Any idea where the compilable files are in the boost library?
All the code compiles. If you mean cpp files, then it is true, many libraries are header file only and don't need to be compiled seperate from the project code that uses them. Just include the headers to use them.
Libraries like Filesystem and Serialization have to be compiled. If there are no .cpp files then how do you tell which files need to be compiled and which ones don't? Ryan

On Wed, Mar 16, 2011 at 8:24 PM, Ryan
Libraries like Filesystem and Serialization have to be compiled. If there are no .cpp files then how do you tell which files need to be compiled and which ones don't?
The "Boost getting started guide" has all the information you need about the directory layout and the procedure to compile and install the Boost libraries: http://www.boost.org/doc/libs/1_46_1/more/getting_started/index.html Best regards, Riccardo

//File A.hpp
class B;
class A {
double x;
public:
B inline convertToB(void);
};
#include

Ryan wrote:
//File A.hpp class B;
class A { //... };
#include
//File B.hpp class A;
class B { //... };
#include
//File A_detail.hpp #include
B A::convertToB(void) {/*...*/}
//File B_detail.hpp #include
A B::convertToA(void) {/*...*/}
Why not use the file structure that Luke proposed 36 minutes before your last email? -Julian

On Wed, Mar 16, 2011 at 4:51 PM, Julian Gonggrijp
Why not use the file structure that Luke proposed 36 minutes before your last email?
I tried using the file structure and it didn't compile. I then modified it to be more in line with what I need and it still didn't compile. At that point I wanted to get more help. Ryan

From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Ryan
The compiler is giving the error that it can't access the private members. Once the forward declaration is used the compiler can't seem to use the full class later.
class B; class A { double x; public: inline A(double x_):x(x_){} B inline convertToB(void); }; class A; class B { double y; public: inline B(double y_):y(y_){} A inline convertToA(void); }; B A::convertToB(void) { return B(x); } A B::convertToA(void) { return A(y); } int main() {return 0;} Your problem was that you included A_detail before you defined class B. The above code compiles. Regardless of what files you put it in you have to include them in the right order or you will get errors. Include them in the order such as that functions that depend on both A and B are defined after both A and B are defined. By the way, for functions defined in a header file without inline keyword the compiler has the option to inline them. Only if the compiler chooses not to inline the function will it caues a link error if there are multiple object files that contain that function definition. It is very easy to ship broken code that compiles and links because you are lucky and functions defined in header files without the inline keyword are inlined. If you specify the inline keyword the compiler still has the option not to actually inline them, but if it does so it makes them a weak symbol instead of a strong symbol, which allows the linker to choose any one of multiple possible duplicate weak symbol definitions of the same function in different object files, whereas a strong symbol will result in a duplicate function definition error. Regards, Luke

AMDG On 03/16/2011 03:09 PM, Simonson, Lucanus J wrote:
By the way, for functions defined in a header file without inline keyword the compiler has the option to inline them. Only if the compiler chooses not to inline the function will it caues a link error if there are multiple object files that contain that function definition.
Doesn't the compiler have to emit a definition even if it decides to inline it? In Christ, Steven Watanabe

Steven Watanabe wrote:
AMDG
On 03/16/2011 03:09 PM, Simonson, Lucanus J wrote:
By the way, for functions defined in a header file without inline keyword the compiler has the option to inline them. Only if the compiler chooses not to inline the function will it caues a link error if there are multiple object files that contain that function definition.
Doesn't the compiler have to emit a definition even if it decides to inline it?
I suppose it depends upon the compiler. My experience was that I don't get the link error if I omit the inline keyword if the compiler fully inlines all usage of the function and in that case there is neither strong nor weak symbol for the function in the symbol table of the object file and therefore no link error later. However, as soon as the compiler doesn't inline the function somewhere in the object file it shows up in the symbol table and results in link error if it is also in the symbol table of another object file and the inline keyword was omitted resulting in strong symbol. That is why I declare all header file function definitions inline so that link errors don't sneak up on me. You always have the symbol if you compile in debug mode, of course, so the link error will show up right away in the debug build, but not always in the optimized build. The normal way I check how successful the compiler is in inlining functions I intend to be inlined (including template functions) is check for the existence of the weak symbol in the object file of an optimized build. I have seen huge volumes of my code compiled into a benchmark application with only main() in the symbol table. Profiling at that point turned out to be useless because 100% of runtime was attributed to main. Since function call overhead was the thing we were trying to optimize, however, we were satisfied with that result. By the way, avoiding calling the same function you want to be inlined multiple times from another function, minimizing the call stack depth of functions you want inlined and passing even built in types by reference instead of by value seem to be the most important factors in determining whether functions get inlined as desired. Since we rely on the compiler to inline template code and make our abstraction zero cost it becomes important to understand why the compiler fails, at least empirically, so that we can change our code to reclaim that performance loss. Here we are talking specifically about and older version of gcc, but in general all compilers follow the same heuristics to try to balance performance improvement, object code size and compile time. Regards, Luke

Thank you both for your insight into the workings of the compiler. Ryan

On 16/03/2011 18:45, Ryan wrote:
In the boost library system certain libraries need to be compiled and linked into a project. For example, libraries would need to be compiled to remove cyclic header dependencies. In the boost library system I coundn't find any .cpp files that would indecate a compilable library. So how do library developers create compilable files in the boost libraries? The following two files would need to be broken up and compiled to remove the dependencies, correct? Ryan //File A.hpp class B; class A { double x; public: B convertToB(void) { return B(x); } }; //File B.hpp class A; class B { double y; public: A convertToA(void) { return A(y); } };
Just move the definitions of convertToB and convertToB to other files. Whether they're headers or not is irrelevant (you'll need to qualify inline explicitly if they're in headers though).
participants (6)
-
Julian Gonggrijp
-
Mathias Gaunard
-
Riccardo Murri
-
Ryan
-
Simonson, Lucanus J
-
Steven Watanabe