0

I'm trying to implement a library that uses the rather popular C++ header paradigm (separating definition and declaration in a .h / .hpp and a .cpp file). I'm using the MSVC cl.exe compiler to target the Windows operating system.

I explicitly use the #include directive targeting the header file abc.hpp where the definition is expressed. I expect the compiler to automatically look for the corresponding abc.cpp file and redirect calls to abc.hpp::abc() towards abc.cpp::abc().

(I know I could directly #include "abc.cpp" in the example but remember I'm trying to solve a bigger problem that is include an entire library from it's header files.)

(I also tried to use the /I compiler option but after both testing and reading the documentation it's not of much use.) https://learn.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories

I described the test I run bellow and the compiler error I get:

######## main.cpp ######## 
#include <stdio.h> // printf_s
#include "abc.hpp"
int main(int argc, char *argv[]) {
    abc();
    return(0);
}

######## abc.hpp ######## 
void abc();

######## abc.cpp ######## 
#include "abc.hpp"

void abc() {
    printf("ABC!\n");
}

######## command line ########
> cl main.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.X for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

main.cpp
Microsoft (R) Incremental Linker Version 14.X
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:main.exe
main.obj
main.obj : error LNK2019: unresolved external symbol "void __cdecl abc(void)" (?abc@@YAXXZ) referenced in function main
main.exe : fatal error LNK1120: 1 unresolved externals

Is my expectation about the compiler automatically remapping abc.hpp::abc() towards abc.cpp::abc() off the mark? Am I forced to have the .cpp code be compiler as a separate .lib to be able to /link /LIBPATH: it to the compiler?

I'd be pleased to hear your insight about this matter.

Mendear
  • 25
  • 5
  • 1
    You need to compile both `main.cpp` and `abc.cpp` and then link them together to make the final executable. Please explain why you want to use the command line rather than the IDE? Tempted to flag as a duplicate https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix – Richard Critten Nov 23 '19 at 18:33
  • try ```cl main.cpp abc.cpp```.. it will link two cpp into one exe. – Peter Lee Nov 23 '19 at 18:39
  • Are you telling me to make a unity build of both main.cpp and abc.cpp? If so I would have to make an entire unity build for the whole library. Which is doable but I expected libraries to be easier to use. Also not many people uses unity build so I am skeptical as of people using that strategy in order to use this archetype of library. (I'm trying to use the `glslang` library.) > Why use the command line rather than the IDE? Because all I want to is compile. I don't need visual studio to add hidden files and folder to my project. – Mendear Nov 23 '19 at 18:40
  • (1) No one is suggesting a Unity Build. (2) where does a library come into this (not in the question). (3) you seem to mixing up basic compile, link terminology. – Richard Critten Nov 23 '19 at 18:43
  • @PeterLee `cl main.cpp abc.cpp` doesn't quite seem to be the solution here. ```main.cpp main.cpp(6): error C3861: 'abc': identifier not found abc.cpp abc.cpp(5): error C3861: 'printf': identifier not found Generating Code...``` Also I don't expect myself to write the path of every single `.cpp` file this library contains within this command. – Mendear Nov 23 '19 at 18:45
  • Possible duplicate of [What is an undefined reference/unresolved external symbol error and how do I fix it?](https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Richard Critten Nov 23 '19 at 18:46
  • @RichardCritten then I did not understand what you meant by compiling both `.cpp` file then linking them together. Would you mind describing the process by an example? – Mendear Nov 23 '19 at 18:46
  • @RichardCritten Please read the first line of the question again you might have missed it. – Mendear Nov 23 '19 at 18:47
  • @RichardCritten Would you mind pointing out "basic compile, link terminology" I "seem to mix[...] up"? – Mendear Nov 23 '19 at 18:51
  • @Mendear Such feature is not supported by MSVC.. MSVC expect you to write every signle cpp path within command line.. That is why peoples use IDE, Makefile, etc – Peter Lee Nov 23 '19 at 18:54
  • 2
    "I expect the compiler to automatically look for the corresponding abc.cpp file" thats not how it works – 463035818_is_not_an_ai Nov 23 '19 at 18:55
  • Thank you @formerlyknownas_463035818 that answer one of the question, would you mind answering the second question? "Am I forced to have the .cpp code be compiler as a separate .lib to be able to /link /LIBPATH: it to the compiler?" – Mendear Nov 23 '19 at 18:58
  • you need to include, `` in `abc.h` or `abc.cpp` then I would expect `cl main.cpp abc.cpp` to work. The error you quote in a comment is due to `printf` not `abc` – 463035818_is_not_an_ai Nov 23 '19 at 19:00
  • @formerlyknownas_463035818 You are absolutely right. I assume main.cpp and abc.cpp are compiled independently before being linked together, which would explains why you need to include it for each file. In the case I want to use a library that contains a lot of those kind of `.cpp` files including `.h` and `.hpp` files I need to list those `.cpp` after the `cl` call? Or is there a more efficient option? – Mendear Nov 23 '19 at 19:10

1 Answers1

0

You have a ... let's say more reasonable opinion on how C++ compile and linking should work. But the truth is that the C++ include compile and link model is ancient and seems alien to any modern modular builds in modern languages. However it is so encarved into C++, that is the way it goes with C++ and that was the way since the beginning. Things are about to change (slowly) with modules being introduced into the standard in C++20 (really a great and arduous achievement considering C++'s legacy and backwards-compatibility).


So the classical way is this: You include headers that contain the declaration you need. #include is a preprocessor directive and it is more-or-less just a dumb copy-paste of the contents of the header. This lets you compile each "compilation unit" individually. Then you link all of the compilation units into your final binary (bee it a library or an executable).

Let's say you have a foo.cpp with the definition of the function foo.

foo.cpp
int foo(int a)
{
     return a * a;
}

If you need to call foo from another compilation unit you first need to make the declaration of foo visible in that compilation unit. You can do that easily just by writing the declaration:

bar.cpp
int foo(int);

void bar()
{
    int r = foo(24);
}

Before using foo the compiler need to know what foo is. Aka it needs to know it is a function taking an int parameter and returning an int. That is what I did in the example above when I wrote int foo(int). Hower you shouldn't write each declaration. You can write the declaration of foo once in foo.hpp and then include that header:

foo.hpp
#pragma once // or standard-compliant guard

int foo(int);
bar.cpp
#include "foo.hpp"

void bar()
{
    int r = foo(24);
}

The preprocessor wil textually replace #include "foo.hpp" with the contents of the file before feeding it to the compiler.

Now you can compile bar.cpp into an object file. The compiler just writes a call to a symbol foo known to name a function taking an int parameter and returning an int. However the definition of foo is not visible. Aka. the compiler doesn't have the "source code" of foo.

So the next step is to link together all the compilation units.

You can do the compilation and linking in two separate stages (I use g++ in the examples because I am more familiar with the parameters, but the principles are the same for cl.exe):

# compile each compilation unit
g++ bar.cpp -o bar.o
g++ foo.cpp -o foo.o

# link all into the final executable
g++ bar.o foo.o -o my_executable

Or you can do it all in one go:

g++ foo.cpp bar.cpp -o my_executable

Because cl.exe (and clang and gcc) is neither the compiler nor the linker. It's a front end that calls the preprocessor, compiler and/or linker on your behalf.


You need to know these details to understand what is happening under the hood so you can solve issues when they appear, but you shouldn't manually invoke the compiler. Use a build system that will take care of all this for you. Let VS do this for you or use make / cmake.

Community
  • 1
  • 1
bolov
  • 72,283
  • 15
  • 145
  • 224
  • Hello @bolov ! I now understand better the intrinsic of the problem and am now better armed for solving future instances of that same problem archetype. Your explanation, your time and your energy is greatly appreciated! Thank you! – Mendear Nov 23 '19 at 21:42
  • For now I was using my own `.bat` file to describe my compilation needs. Up until now is was enough for my own code and the single file libraries I was using. The need for a tool like make / cmake now arises from the desire to use some third party libraries. It seems that I can't put aside this complexity anymore. I'll give a go at cmake up until I get a better understanding of what I'm working with, glslang was made with make / cmake in mind anyway. Again, thank you for your insight. – Mendear Nov 23 '19 at 21:52
  • @Mendear you are welcome. Consider upvoting if it helped you and accepting an answer. – bolov Nov 23 '19 at 21:53
  • I did ! However : "Thanks for the feedback! Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score." – Mendear Nov 23 '19 at 21:54