Administrator
Administrator
发布于 2025-04-09 / 0 阅读

C++编译成lib静态库

C++编译成lib静态库

静态库(Static Library)是一种在编译时直接嵌入到程序中的库。
动态库(Dynamic Library)是一种在程序运行时被加载的库。
动态库与静态库的区别

注意:python无法直接调用静态库,因为静态库(.lib)在编译阶段将代码直接嵌入到宿主程序中,而 Python 作为解释型语言,无法在运行时动态链接静态库。需要将静态库封装成动态库后,python才可以调用

在 Visual Studio 中生成静态库(.lib)的完整步骤

步骤 1:创建静态库项目

  1. 打开 Visual Studio → Create a new project → 搜索 静态库(Lib) → 命名为 GeometryStaticLib → 创建。
  2. 删除默认生成的 pch.h, framework.h, dllmain.cpp 等文件,右键项目 → AddNew Item → 创建 Geometry.cppGeometry.h。(注意:生成静态库,需要头文件。而动态库如果是python调用则不需要,如果是c++调用动态库也是需要头文件)

步骤 2:编写代码

Geometry.h(头文件)

#pragma once

// 声明函数为 extern "C" 以避免名称修饰(Name Mangling)
#ifdef __cplusplus
extern "C" {
#endif

    // 基础函数
    float distance_sq_segment(
        const float* point,
        const float* v1,
        const float* v2
    );

    // 主功能函数
    bool triangleInsideSphere(
        const float* point,
        float radiusSq,
        const float* v1,
        const float* v2,
        const float* v3
    );

    bool pointInsideTriangle(
        const float* point,
        const float* v1,
        const float* v2,
        const float* v3
    );

#ifdef __cplusplus
}
#endif

Geometry.cpp(与之前的 DLL 代码相同,无需 __declspec(dllexport)):

#include "Geometry.h"
#include <cmath>
// --------------------- 基础函数 ---------------------
float distance_sq_segment(
    const float* point,
    const float* v1,
    const float* v2
) {
    float v2v1[3] = { v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2] };
    float len_sq = v2v1[0] * v2v1[0] + v2v1[1] * v2v1[1] + v2v1[2] * v2v1[2];

    if (len_sq < 1e-20f) {
        float dx = point[0] - v1[0], dy = point[1] - v1[1], dz = point[2] - v1[2];
        return dx * dx + dy * dy + dz * dz;
    }

    float pt[3] = { point[0] - v1[0], point[1] - v1[1], point[2] - v1[2] };
    float t = (pt[0] * v2v1[0] + pt[1] * v2v1[1] + pt[2] * v2v1[2]) / len_sq;

    if (t < 0.0f) {
        return pt[0] * pt[0] + pt[1] * pt[1] + pt[2] * pt[2];
    }
    else if (t > 1.0f) {
        float dx = point[0] - v2[0], dy = point[1] - v2[1], dz = point[2] - v2[2];
        return dx * dx + dy * dy + dz * dz;
    }
    else {
        float proj[3] = { pt[0] - t * v2v1[0], pt[1] - t * v2v1[1], pt[2] - t * v2v1[2] };
        return proj[0] * proj[0] + proj[1] * proj[1] + proj[2] * proj[2];
    }
}

// --------------------- 主功能函数 ---------------------
bool triangleInsideSphere(
    const float* point,
    float radiusSq,
    const float* v1,
    const float* v2,
    const float* v3
) {
    float d1 = distance_sq_segment(point, v1, v2);
    float d2 = distance_sq_segment(point, v2, v3);
    float d3 = distance_sq_segment(point, v1, v3);
    return (d1 < radiusSq) || (d2 < radiusSq) || (d3 < radiusSq);
}

bool pointInsideTriangle(
    const float* point,
    const float* v1,
    const float* v2,
    const float* v3
) {
    const float eps = 1e-10f;
    const float* A = v1;
    const float* B = v2;
    const float* C = v3;
    const float* P = point;

    // 计算向量
    float vec0[3] = { C[0] - A[0], C[1] - A[1], C[2] - A[2] };
    float vec1[3] = { B[0] - A[0], B[1] - A[1], B[2] - A[2] };
    float vec2[3] = { P[0] - A[0], P[1] - A[1], P[2] - A[2] };

    // 计算点积
    float dot00 = vec0[0] * vec0[0] + vec0[1] * vec0[1] + vec0[2] * vec0[2];
    float dot01 = vec0[0] * vec1[0] + vec0[1] * vec1[1] + vec0[2] * vec1[2];
    float dot02 = vec0[0] * vec2[0] + vec0[1] * vec2[1] + vec0[2] * vec2[2];
    float dot11 = vec1[0] * vec1[0] + vec1[1] * vec1[1] + vec1[2] * vec1[2];
    float dot12 = vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2];

    // 计算重心坐标
    float denom = dot00 * dot11 - dot01 * dot01;
    if (std::fabs(denom) < eps) return false;

    float inv_denom = 1.0f / denom;
    float u = (dot11 * dot02 - dot01 * dot12) * inv_denom;
    float v_val = (dot00 * dot12 - dot01 * dot02) * inv_denom;

    return (u >= -eps) && (v_val >= -eps) && (u + v_val <= 1.0f + eps);
}

步骤 3:配置项目属性

  1. 选择目标平台
    • 顶部工具栏选择 Releasex64(与 Python 位数一致)。
  2. 设置静态库编译选项
    • 右键项目 → 属性(Properties)配置属性(Configuration Properties)常规(General)配置类型(Configuration Type) 选择 Static Library (.lib)
  3. 调整运行时库
    C/C++代码生成(Code Generation)运行库(Runtime Library) 选择 /MT(静态链接运行时库)。
  4. 禁用预编译头
    C/C++ → 预编译头(Precompiled Headers) → 预编译头(Precompiled Header) 选择 不使用预编译头(Not Using Precompiled Headers)

步骤 4:编译生成静态库

  1. 右键项目 → Build,成功后在 x64/Release/ 目录下生成 GeometryStaticLib.lib
  2. 重要区别
    • 静态库 .lib 文件比动态库的 .lib(导入库)大,因为它包含所有代码。

步骤 5:测试静态库

  1. 创建测试项目
    • 新建 Console Application 项目 → 命名为 TestStaticLib
  2. 配置静态库链接
    • 右键测试项目 → PropertiesLinkerInputAdditional Dependencies → 添加 GeometryStaticLib.lib 的完整路径。
    C/C++GeneralAdditional Include Directories → 添加 Geometry.h 所在目录。
  3. 测试代码
#include <iostream>
#include "Geometry.h"

int main() {
    float point[3] = {0.5f, 0.5f, 0.0f};
    float v1[3] = {1.0f, 0.0f, 0.0f};
    float v2[3] = {0.0f, 1.0f, 0.0f};
    float v3[3] = {0.0f, 0.0f, 1.0f};
    float radiusSq = 0.5f;

    bool in_sphere = triangleInsideSphere(point, radiusSq, v1, v2, v3);
    bool in_triangle = pointInsideTriangle(point, v1, v2, v3);

    std::cout << "In sphere: " << in_sphere << ", In triangle: " << in_triangle << std::endl;
    return 0;
}
  1. 编译运行,应输出 In sphere: 1, In triangle: 1

关键区别:静态库 vs 动态库

特性 静态库(.lib) 动态库(.dll + 导入库 .lib)
代码嵌入方式 链接时完全嵌入主程序 运行时动态加载
文件部署 无需额外文件 需分发 .dll
体积 主程序体积较大 主程序体积较小
内存占用 无法共享代码 多个程序可共享同一 .dll
更新维护 需重新编译主程序 只需替换 .dll

总结

• 静态库适合需要代码封闭性避免依赖外部文件的场景。
• 通过上述步骤,你可以将现有代码无缝转换为静态库,并在其他 C++ 项目中直接链接使用。
• Python 无法直接调用静态库(.lib)文件,因为静态库需要在编译时链接到宿主程序(如另一个 DLL 或 EXE)。若想通过 Python 调用静态库中的函数,必须将静态库封装成动态库(.dll),再通过 ctypes 调用。

将静态库封装为动态库

步骤 1:创建动态库项目

  1. 在 Visual Studio 中新建 Dynamic-Link Library (DLL) 项目。
  2. 添加封装代码,调用静态库中的函数:
// Wrapper.cpp
#include "StaticLib.h"  // 静态库的头文件

extern "C" __declspec(dllexport) bool triangleInsideSphere(
    const float* point, float radiusSq, const float* v1, const float* v2, const float* v3
) {
    return ::triangleInsideSphere(point, radiusSq, v1, v2, v3);  // 调用静态库函数
}

extern "C" __declspec(dllexport) bool pointInsideTriangle(
    const float* point, const float* v1, const float* v2, const float* v3
) {
    return ::pointInsideTriangle(point, v1, v2, v3);
}

步骤 2:链接静态库

  1. 项目属性LinkerInputAdditional Dependencies → 添加 YourStaticLib.lib
  2. 项目属性C/C++GeneralAdditional Include Directories → 添加静态库头文件路径。

步骤 3:生成动态库

  1. 编译生成 Wrapper.dll,该 DLL 会嵌入静态库代码。

Python 调用动态库

import ctypes
import numpy as np

# 加载动态库
lib = ctypes.CDLL('./Wrapper.dll')

# 配置函数参数类型 (示例)
lib.triangleInsideSphere.argtypes = [
    np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C'),
    ctypes.c_float,
    np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C'),
    np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C'),
    np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C')
]
lib.triangleInsideSphere.restype = ctypes.c_bool

# 调用函数
point = np.array([0.5, 0.5, 0.0], dtype=np.float32)
v1 = np.array([1.0, 0.0, 0.0], dtype=np.float32)
v2 = np.array([0.0, 1.0, 0.0], dtype=np.float32)
v3 = np.array([0.0, 0.0, 1.0], dtype=np.float32)
radius_sq = 0.5

result = lib.triangleInsideSphere(point, radius_sq, v1, v2, v3)
print(result)  # 输出: True

性能与开发成本对比

方法 性能 开发复杂度 适用场景
静态库 + 封装 DLL 需复用现有静态库代码
直接编写动态库 新项目开发
Numba JIT 快速迭代

总结

直接调用静态库不可行:Python 无法绕过动态库机制。
最佳实践:将静态库封装为动态库后,通过 ctypes 调用,既保留静态库的代码复用性,又兼容 Python 的灵活性。