【发布时间】:2021-01-09 15:25:29
【问题描述】:
我在将我的 C++ 库重写为模板形式时遇到了困难;主要问题涉及重新设计Makefile。
在以前的状态下,当它是非模板时,我有:
- 一个头文件lib.h,其中(受包含保护)我们有类和重载运算符的声明。此文件根本不包含任何其他库。
- 一个实现文件lib.cpp,在该文件的顶部,我包含了来自标准库(cmath、iostream 等)的许多头文件,以及为此包含的头文件自定义库:
#include "lib.h" - 一个Makefile,带有用于构建/安装库的命令:
CC = g++
CFLAGS = -O2
SRC = lib.cpp
HDR = $(SRC:.cpp=.h)
OBJ = $(SRC:.cpp=.o)
LIB = $(OBJ:.o=.a)
.PHONY: all install clean uninstall
# =========================================================
# Build
# =========================================================
all: $(LIB)
# Create an object file
$(OBJ):
$(CC) $(CFLAGS) -c -o $@ $(SRC)
# Create a static library file
$(LIB): $(OBJ)
ar rcs $@ $<
# =========================================================
# Install
# =========================================================
install: ~/lib/lib/$(LIB) ~/lib/include/$(HDR)
# Create top-level directory for the libraries
~/lib:
mkdir -p $@;
# Create top-level directory for the static library files
~/lib/lib:
mkdir -p $@;
# Create top-level directory for the headers
~/lib/include:
mkdir -p $@;
# Copy the library file into the right directory
~/lib/lib/$(LIB): $(LIB) ~/lib/lib
cp $< $@
# Copy the header file into the right directory
~/lib/include/$(HDR): $(HDR) ~/lib/include
cp $< $@
- 安装后,我的 CI (GH Actions) 将编译一个小型测试程序,其中包括我的库头 (
#include <lib.h>),使用以下命令:
g++ -O0 -Wall --std=c++14 test.cpp -I ~/lib/include/ ~/lib/lib/lib.a -o test
此设置运行良好。
现在问题出现了,当我想将我的类重写为模板时。
按照in this post 的信息,我在我的库的头文件末尾添加了一个#include "lib.cpp"(在include 保护内,ofc)。通过该更改,我需要调整编译过程并且不要在命令行中提供我的实现文件(因为它已经包含在标头中,我必须避免重新定义错误)。这很好用。问题的核心现在在Makefile 中,在构建目标文件的命令中。当我尝试编译我的库的实现文件时,它包含标头,并且标头再次包含实现...我对this port 中的问题表示怀疑,他们建议从实现文件中删除标头包含。所以我这样做了,我评论了#include "lib.h"并尝试运行:
g++ -O2 -c -o lib.o lib.cpp
我最终会遇到很多 error: use of undeclared identifier 错误...
如何使用make 正确构建库?我的限制是库保留在两个单独的文件中:头文件和实现。我最终想要的是能够在我的进一步程序中#include <lib.h>。
甚至可以创建归档对象文件吗? 共享对象库(.so)呢
【问题讨论】:
-
使用典型的模板库,一切都在标题中。没有什么可构建或链接的。另请参阅:header-only library。为什么又要坚持拥有两个单独的文件?
-
不可能将模板代码作为目标文件分发,除非您使用显式模板实例化(这是对库的实用性的重大限制)。试图通过在头文件中包含 cpp 文件来伪造它没有任何好处。你应该像其他人一样做一个只有标题的库。
-
我的库的编译可能需要太多时间,只有标头的版本需要在每次下游发生任何变化时重新编译整个东西,我想避免这种情况。
-
这就是模板库的本质——在使用特定的模板参数集实例化模板之前,无法编译代码。你不能同时拥有模板和单独的编译;选一个。将
.cpp文件包含到.h文件中不会为您提供单独的编译 - 每当.cpp文件更改时,包含它的所有内容(间接通过.h文件)都需要重新编译。该文件具有.cpp扩展名而不是.h并没有改变这一事实 - 文件扩展名只是为了方便人类的命名约定,它们不具有神奇的属性 -
这就是 模板 的本质,句号。您不能将模板编译为目标代码,只能将它们的实例化。正是使用库的代码决定了需要哪些实例化。是的,这就是 C++ 编译通常比 C 编译成本更高的原因之一。
标签: c++ templates makefile shared-libraries static-libraries