MSVC cl.exe 尝试使用 C++ header 范式(定义与声明)
MSVC cl.exe trying to use C++ header paradigm (definition vs declaration)
我正在尝试实现一个使用相当流行的 C++ header 范例的库(在 .h / .hpp 和 .cpp 文件中分离定义和声明)。
我正在使用 MSVC cl.exe 编译器来针对 Windows 操作系统。
我明确使用 #include
指令,目标是 header 文件 abc.hpp
,其中表达了定义。我希望编译器自动查找相应的 abc.cpp
文件并将对 abc.hpp::abc()
的调用重定向到 abc.cpp::abc()
.
(我知道我可以在示例中直接 #include "abc.cpp"
但请记住,我正在尝试解决一个更大的问题,即从 header 文件中包含整个库。)
(我也尝试使用 /I
编译器选项,但在测试和阅读文档后它并没有多大用处。)
https://docs.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories
我在下面描述了我 运行 的测试和我得到的编译器错误:
######## 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
我对编译器自动将 abc.hpp::abc()
重新映射到 abc.cpp::abc()
的期望是否偏离了目标?
我是否被迫将 .cpp
代码作为单独的 .lib
进行编译,以便能够 /link /LIBPATH:
编译器?
很高兴听到您对此事的见解。
你有一个...让我们对 C++ 编译和 linking 应该如何工作有更合理的意见。但事实是,C++ include compile and link 模型是古老的,似乎与现代语言中的任何现代模块化构建都格格不入。然而,它是如此深入到 C++ 中,这就是它与 C++ 一起使用的方式,而且从一开始就是这样。随着模块被引入 C++20 的标准,事情即将发生变化(缓慢地)(考虑到 C++ 的遗产和 backwards-compatibility,这确实是一项伟大而艰巨的成就)。
所以经典的方法是这样的:你包含包含你需要的声明的 headers。 #include
是一个预处理器指令,它 more-or-less 只是 header 内容的哑 copy-paste。这使您可以单独编译每个“编译单元”。然后你 link 将所有编译单元放入你的最终二进制文件(无论是库还是可执行文件)。
假设您有一个 foo.cpp
,其函数定义为 foo
。
foo.cpp
int foo(int a)
{
return a * a;
}
如果您需要从另一个编译单元调用 foo
,您首先需要使 foo
的声明在该编译单元中可见。您只需编写声明即可轻松做到这一点:
bar.cpp
int foo(int);
void bar()
{
int r = foo(24);
}
在使用 foo
之前,编译器需要知道 foo
是什么。也就是说,它需要知道它是一个接受 int 参数并返回 int 的函数。这就是我在上面的例子中写 int foo(int)
时所做的。但是您不应该写每个声明。您可以在 foo.hpp
中写一次 foo
的声明,然后包含 header:
foo.hpp
#pragma once // or standard-compliant guard
int foo(int);
bar.cpp
#include "foo.hpp"
void bar()
{
int r = foo(24);
}
预处理器将在将 #include "foo.hpp"
提供给编译器之前用文件内容替换 #include "foo.hpp"
。
现在您可以将 bar.cpp
编译成 object 文件。编译器只是编写一个对符号 foo
的调用,该符号已知用于命名一个接受 int 参数并返回 int 的函数。但是 foo
的定义是不可见的。阿卡。编译器没有 foo
.
的“源代码”
所以下一步就是link所有的编译单元。
编译和linking可以分两个阶段进行(我在例子中使用g++
是因为我对参数比较熟悉,但是[=的原理是一样的69=]):
# 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
或者您可以一次完成所有操作:
g++ foo.cpp bar.cpp -o my_executable
因为 cl.exe
(以及 clang
和 gcc
)既不是编译器也不是 linker。它是一个代表您调用预处理器、编译器 and/or linker 的前端。
您需要了解这些详细信息以了解幕后发生的事情,以便在问题出现时解决问题,但您不应手动调用编译器。使用一个可以为您处理所有这些的构建系统。让 VS 为你做这个或使用 make / cmake。
我正在尝试实现一个使用相当流行的 C++ header 范例的库(在 .h / .hpp 和 .cpp 文件中分离定义和声明)。 我正在使用 MSVC cl.exe 编译器来针对 Windows 操作系统。
我明确使用 #include
指令,目标是 header 文件 abc.hpp
,其中表达了定义。我希望编译器自动查找相应的 abc.cpp
文件并将对 abc.hpp::abc()
的调用重定向到 abc.cpp::abc()
.
(我知道我可以在示例中直接 #include "abc.cpp"
但请记住,我正在尝试解决一个更大的问题,即从 header 文件中包含整个库。)
(我也尝试使用 /I
编译器选项,但在测试和阅读文档后它并没有多大用处。)
https://docs.microsoft.com/en-us/cpp/build/reference/i-additional-include-directories
我在下面描述了我 运行 的测试和我得到的编译器错误:
######## 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
我对编译器自动将 abc.hpp::abc()
重新映射到 abc.cpp::abc()
的期望是否偏离了目标?
我是否被迫将 .cpp
代码作为单独的 .lib
进行编译,以便能够 /link /LIBPATH:
编译器?
很高兴听到您对此事的见解。
你有一个...让我们对 C++ 编译和 linking 应该如何工作有更合理的意见。但事实是,C++ include compile and link 模型是古老的,似乎与现代语言中的任何现代模块化构建都格格不入。然而,它是如此深入到 C++ 中,这就是它与 C++ 一起使用的方式,而且从一开始就是这样。随着模块被引入 C++20 的标准,事情即将发生变化(缓慢地)(考虑到 C++ 的遗产和 backwards-compatibility,这确实是一项伟大而艰巨的成就)。
所以经典的方法是这样的:你包含包含你需要的声明的 headers。 #include
是一个预处理器指令,它 more-or-less 只是 header 内容的哑 copy-paste。这使您可以单独编译每个“编译单元”。然后你 link 将所有编译单元放入你的最终二进制文件(无论是库还是可执行文件)。
假设您有一个 foo.cpp
,其函数定义为 foo
。
foo.cpp
int foo(int a)
{
return a * a;
}
如果您需要从另一个编译单元调用 foo
,您首先需要使 foo
的声明在该编译单元中可见。您只需编写声明即可轻松做到这一点:
bar.cpp
int foo(int);
void bar()
{
int r = foo(24);
}
在使用 foo
之前,编译器需要知道 foo
是什么。也就是说,它需要知道它是一个接受 int 参数并返回 int 的函数。这就是我在上面的例子中写 int foo(int)
时所做的。但是您不应该写每个声明。您可以在 foo.hpp
中写一次 foo
的声明,然后包含 header:
foo.hpp
#pragma once // or standard-compliant guard
int foo(int);
bar.cpp
#include "foo.hpp"
void bar()
{
int r = foo(24);
}
预处理器将在将 #include "foo.hpp"
提供给编译器之前用文件内容替换 #include "foo.hpp"
。
现在您可以将 bar.cpp
编译成 object 文件。编译器只是编写一个对符号 foo
的调用,该符号已知用于命名一个接受 int 参数并返回 int 的函数。但是 foo
的定义是不可见的。阿卡。编译器没有 foo
.
所以下一步就是link所有的编译单元。
编译和linking可以分两个阶段进行(我在例子中使用g++
是因为我对参数比较熟悉,但是[=的原理是一样的69=]):
# 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
或者您可以一次完成所有操作:
g++ foo.cpp bar.cpp -o my_executable
因为 cl.exe
(以及 clang
和 gcc
)既不是编译器也不是 linker。它是一个代表您调用预处理器、编译器 and/or linker 的前端。
您需要了解这些详细信息以了解幕后发生的事情,以便在问题出现时解决问题,但您不应手动调用编译器。使用一个可以为您处理所有这些的构建系统。让 VS 为你做这个或使用 make / cmake。