Search code examples
c++cerror-handlingcompilationlinker

Making linker errors more helpful through specificity (C/C++)


This is my first time asking a question here, as I'm usually able to find answers in previous posts, but I can't find any information on this topic.

I'm trying to write C/C++ header files that can be reused between projects. One of my headers uses <math.h> (deprecated in C++, so it uses <cmath> instead). When I compile a C++ program to test the header, it works perfectly. When I compile a C program to test the header, the linker rightfully throws a fit unless "libm.a" is linked ("-l m" in most compilers). I'm not worried about myself forgetting to link the library, but I plan on making these headers available for public use, and I'd like to print a more helpful error message than "undefined reference". Here's my question, is there any way for me to use something akin to preprocessor directives to check if a specific library is linked properly when building the executable so I can throw my own error message?

Source code: https://github.com/LimikEcho/rlx/blob/main/itosa.h

I've tried checking if specific macros are defined (from both <math.h> and <math.c>). If I check a macro defined in <math.h>, it returns 1 regardless of linking "libm.a". If I check a macro defined in <math.h>, it returns 0 regardless of linking "libm.a". I understand why that is, but it was worth a shot.

Edit: People seem to be missing the point of this, so let me reiterate. I want to know if it's possible to influence the error handling of the linker from a header, that way whenever it's used by third parties, they can be accurately warned about missing a specific library. I want to display undefined reference errors as something like undefined reference to 'example_function', did you forget to link 'libm.a'?


Solution

  • The build process runs in different phases. The compilation phase takes your program code including the header file and produces a so called object files (with the file extension .o).

    The same applies for your library. You get a set of object files. When you tell the compiler to produce a static library it takes a set of object files and put them all together into a static library (with the file extension .a).

    When everything is compiled the linking phase is started. In that phase the object files and the static library is combined into one executable file.

    That means, there is no way to check at the compilation phase the existence of the static library file. Because that file comes into game at the linking phase. The compilation needs only the header file. And this was not your question.

    If a mechanism for loading libraries is needed, you must use a build tool like make. These give you the comfort you are asking for. But such tools normally don't have the purpose to integrate foreign static libraries, although this should be possible.

    But what you try to achieve is also not advisable. It is not guaranteed that it will work if you and who uses the static library uses different versions of the compiler or different compiler options. The code of a static library is directly integrated into a binary. The linker integrates it for you. This requires 100% compatibility between both compilers. As far as I know, there is no mechanism that protects you from incompatibilities. Your use case is relatively exotic. It is no simple question to tell you what could go wrong with it (see also this not answered question: Static link library compatibility with newer compiler versions).

    If you don't want to deliver the source code of your library, then you should consider shared libraries. Shared libraries are exactly made for the purpose you are asking for. You link them at runtime. The operating system does it. And you can also check at runtime if they exist or not.

    On Windows shared libraries have the file extension .dll. On Linux they have the file extension .so.

    For more information see Difference between static and shared libraries? and How do I find out what all symbols are exported from a shared object?.

    Partial Solution

    The following solution could give you at least some sort of indication when forgetting to link against a.

    Your library header a.h is this:

    void please_link_a();
    
    void your_library_function();
    

    Your library code a.c is this:

    void please_link_a(){
      return;
    }
    
    void your_library_function() {
      return;
    }
    

    But then you don't give a.h to the users of your library. Instead you modify it by adding something that triggers a linker warning against please_link_a. So you give a file alib.h with the following contents.

    void please_link_a();
    
    void check_if_a_linked(){
      please_link_a();
    }
    

    What the user now forgets to link the static library, he sees all the linker errors for his code, but in addition this error:

    /usr/bin/ld: /tmp/cczi8mca.o: in function `check_if_a_linked':
    main.c:(.text+0xa): undefined reference to `please_link_a'
    
    

    If it is not your own library, you cannot achieve the please_link_a part of the message. But the check_if_a_linked part you can. Just call anything else from the library. You can always manually change the header file that you got from the author and add such a function. But I don't know if this is practical for you. The linker error is still not so nice.