C、C++ 源文件混合编译合成库

文章目录

  • 混合编译
  • 使用依赖库的情况
  • 使用 `extern C`

其实以前也不怎么关注这点,因为基本上自己写的库和用的第三方依赖大部分都是全 C++ 实现,基本没有 C 实现,但是写算法题的时候还是会经常用到 printf 之类的 C 函数,就很自然而然的去用了,发现编译也能通过就没怎么管。

最近自己移植一个 Unix 上的物理模拟项目,需要用到 glad 之类的 OpenGL 辅助库,突然发觉,这个项目是纯 C 实现的,如果我搬迁到 Windows 上来,Windows 下的 GLFW 和 GLAD 库都好像是 C++ 编译的吧,至少我之前用 OpenGL 的时候都是写 C++ 项目,没有 C 项目。那么这个人用纯 C 实现的例子物理约束模拟也能通过链接,难道是 GLFW 和 GLAD 其实是 C 写的库?

于是尝试写了一个简单的混合编译测试。

混合编译

写一个简单的加法和减法函数,然后加法使用 C++ 编写,减法使用 C 编写,并对加法、减法各自单独编译为一个导出动态库目标,同时再添加一个混合编译目标,混合编译动态库的源文件就是囊括了 add.cppsub.c 的库,我看看如果编译的时候混合了不同类型的源文件,那么编译好的库的导出符号表里面每个函数到底是什么符号。

目录结构如下:

C:.
│  CMakeLists.txt
│
├─build
│  ├─Debug
│  │  ├─bin
│  │  │      libadd.dll
│  │  │      libcalc.dll
│  │  │      libsub.dll
│  │  │
│  │  └─lib
│  │          libadd.dll.a
│  │          libcalc.dll.a
│  │          libsub.dll.a
│  │
│  ├─install
│  │  ├─bin
│  │  │      libadd.dll
│  │  │      libcalc.dll
│  │  │      libsub.dll
│  │  │
│  │  ├─include
│  │  │  ├─add
│  │  │  │      add.h
│  │  │  │
│  │  │  ├─include
│  │  │  │  ├─add
│  │  │  │  │      add.h
│  │  │  │  │
│  │  │  │  └─sub
│  │  │  │          sub.h
│  │  │  │
│  │  │  └─sub
│  │  │          sub.h
│  │  │
│  │  └─lib
│  │      │  libadd.dll.a
│  │      │  libcalc.dll.a
│  │      │  libsub.dll.a
│  │      │
│  │      └─cmake
│  │          └─calc
│  │                  calcConfig.cmake
│  │                  calcConfigVersion.cmake
│  │                  calcTargets-debug.cmake
│  │                  calcTargets.cmake
│
├─cmake
│      calcConfig.cmake.in
│
├─include
│  ├─add
│  │      add.h
│  │
│  └─sub
│          sub.h
│
└─src
        add.cpp
        CMakeLists.txt
        sub.c

对两个函数各自写一个头文件放在 include 目录下对应的子目录里面,这样 CMake 编译的时候好分别为两个目标指定单独的头文件位置。然后 src 目录下分别使用 add.cppsub.c 实现 addsub 函数。

头文件:

// add.h
#ifndef __ADD_H__
#define __ADD_H__

int add(int a, int b);

#endif 

// sub.h
#ifndef __SUB_H__
#define __SUB_H__

int sub(int a, int b);

#endif

源文件:

// add.cpp
#include "add/add.h"

int add(int a, int b) {
    return a + b;
}

// sub.c
#include "sub/sub.h"

int sub(int a, int b) {
    return a - b;
}

然后编译使用 CMake 来构建,项目根目录下的 CMake 内容如下:

cmake_minimum_required(VERSION 3.20)
project(calc VERSION 1.0.0)


set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/lib)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)

include_directories(include)

add_subdirectory(src)

源文件目录 src 的 CMakeLists 如下,负责单独构建 add 目标动态库、sub 目标动态库以及一个混合库目标 calc ,并设置好对应的导出配置,这样 cmake --build build --target install 之后,就会按照导出配置文件,其他项目使用我们这个库的时候可以通过 find_package 轻松引入相关的依赖头文件和依赖库的位置。

# ==================== build add lib ====================
add_library(add SHARED add.cpp)
set_target_properties(
    add PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION}
)
target_include_directories(
    add PUBLIC
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include/add>
    $<INSTALL_INTERFACE:include/add>
)
install(
    TARGETS add
    EXPORT ${PROJECT_NAME}_targets
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION bin
    ARCHIVE DESTINATION lib
    )
install(
    DIRECTORY ${PROJECT_SOURCE_DIR}/include/add
    DESTINATION include
    FILES_MATCHING PATTERN "*.h"
)

# ==================== build sub lib ====================
add_library(sub SHARED sub.c)
set_target_properties(
    sub PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION}
)
target_include_directories(
    sub PUBLIC
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include/sub>
    $<INSTALL_INTERFACE:include/sub>
)
install(
    TARGETS sub
    EXPORT ${PROJECT_NAME}_targets
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION bin
    ARCHIVE DESTINATION lib
    )
install(
    DIRECTORY ${PROJECT_SOURCE_DIR}/include/sub
    DESTINATION include
    FILES_MATCHING PATTERN "*.h"
)

# ==================== build sub lib ====================
add_library(calc SHARED add.cpp sub.c)
set_target_properties(
    calc PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION ${PROJECT_VERSION}
)
target_include_directories(
    calc PUBLIC
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
)
install(
    TARGETS calc
    EXPORT ${PROJECT_NAME}_targets
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION bin
    ARCHIVE DESTINATION lib
    )
install(
    DIRECTORY ${PROJECT_SOURCE_DIR}/include
    DESTINATION include
    FILES_MATCHING PATTERN "*.h"
)



# export to calcTargets.cmake
# 安装库和头文件
include(CMakePackageConfigHelpers)
configure_package_config_file(
    ${CMAKE_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in
    ${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake
    INSTALL_DESTINATION lib/cmake/${PROJECT_NAME}
)

write_basic_package_version_file(
    ${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY AnyNewerVersion
)

install(
    FILES
    ${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}Config.cmake
    ${CMAKE_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake
    DESTINATION lib/cmake/${PROJECT_NAME}
)

# 安装 CMake 配置文件,生成 xxxTargets.cmake 文件
install(
    EXPORT ${PROJECT_NAME}_targets
    FILE ${PROJECT_NAME}Targets.cmake
    NAMESPACE ${PROJECT_NAME}::
    DESTINATION lib/cmake/${PROJECT_NAME}
)

CMake 完成编译和安装之后,在指定的安装目录下会生成复制过来的目标文件以及供其他项目导入此包的 xxxConfig.cmake 文件。

C:.
├─bin
│      libadd.dll
│      libcalc.dll
│      libsub.dll
│
├─include
│  ├─add
│  │      add.h
│  │
│  ├─include
│  │  ├─add
│  │  │      add.h
│  │  │
│  │  └─sub
│  │          sub.h
│  │
│  └─sub
│          sub.h
│
└─lib
    │  libadd.dll.a
    │  libcalc.dll.a
    │  libsub.dll.a
    │
    └─cmake
        └─calc
                calcConfig.cmake
                calcConfigVersion.cmake
                calcTargets-debug.cmake
                calcTargets.cmake

对应每个目标库的头文件也都复制搬运过来了。

使用依赖库的情况

随便编写一个其他的 C++ 和 C 文件,并使用 CMake 构建指定为可执行二进制文件,看看 C 测试文件能不能用 C++ 动态库的函数 add 以及 C++ 测试文件能不能用 C 的动态库函数 sub

首先是准备一个编译两个测试文件所需的 CMakeLists 文件,负责将 cpp 和 c 文件添加为可执行二进制目标,并链接到上述导出库。

cmake_minimum_required(VERSION 3.20)
project(import VERSION 1.0.0)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(DEBUG_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${DEBUG_OUTPUT_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${DEBUG_OUTPUT_DIR}/bin)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${DEBUG_OUTPUT_DIR}/lib)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 14)

find_package(
    calc REQUIRED
    PATHS ../../../addsub/build/install/lib/cmake/calc
)
message("${CALC_INCLUDE_DIRS}")
message("${CALC_LIBRARIES}")
include_directories(${CALC_INCLUDE_DIRS})


add_executable(test-c test-c.c)
target_link_libraries(test-c ${CALC_LIBRARIES})

add_executable(test-cpp test-cpp.cpp)
target_link_libraries(test-cpp ${CALC_LIBRARIES})

set(CMAKE_INSTALL_PREFIX ${DEBUG_OUTPUT_DIR})
foreach(lib IN LISTS CALC_LIBRARIES)
    get_target_property(lib_path ${lib} LOCATION)
    install(
        FILES ${lib_path}
        DESTINATION bin
    )
endforeach(lib IN LISTS CALC_LIBRARIES)

其中的 install 指令能够帮助我们把引入的依赖库目标,提取 Location 属性也就是依赖库文件位置,直接复制到调试文件输出的目录,这样在 Windows 下也能直接 VScode 一键 F5 调试了,不会出现动态库不在二进制可执行文件所在目录下导致调试无法启动。

CMake 配置项目输出:

[main] 正在配置项目: import 
[proc] 执行命令: "C:\Users\fredom\Program Files\MinGW64\mingw64\bin\cmake.EXE" -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=TRUE "-DCMAKE_C_COMPILER:FILEPATH=C:\Users\fredom\Program Files\MinGW64\mingw64\bin\gcc.exe" "-DCMAKE_CXX_COMPILER:FILEPATH=C:\Users\fredom\Program Files\MinGW64\mingw64\bin\g++.exe" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON --no-warn-unused-cli -SC:/Users/fredom/workspace/test/cc/import -Bc:/Users/fredom/workspace/test/cc/import/build -G "MinGW Makefiles"
[cmake] Not searching for unused variables given on the command line.
[cmake] C:/Users/fredom/workspace/addsub/build/install/include/add;C:/Users/fredom/workspace/addsub/build/install/include/sub;C:/Users/fredom/workspace/addsub/build/install/include
[cmake] calc::add;calc::sub;calc::calc
[cmake] -- Configuring done (0.2s)
[cmake] -- Generating done (0.0s)
[cmake] -- Build files have been written to: C:/Users/fredom/workspace/test/cc/import/build

然后 cmake --build build --target install 自动过一遍配置和生成目标,看看能不能走到安装动态库到调试目录这个 make 目标。

[main] 正在生成文件夹: c:/Users/fredom/workspace/test/cc/import/build 
[build] 正在启动生成
[proc] 执行命令: "C:\Users\fredom\Program Files\MinGW64\mingw64\bin\cmake.EXE" --build c:/Users/fredom/workspace/test/cc/import/build --config Debug --target install -j 22 --
[build] [ 25%] Linking C executable Debug\bin\test-c.exe
[build] [ 50%] Linking CXX executable Debug\bin\test-cpp.exe
[build] C:/Users/fredom/Program Files/MinGW64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\test-c.dir/objects.a(test-c.c.obj): in function `main':
[build] C:/Users/fredom/workspace/test/cc/import/test-c.c:8:(.text+0x96): undefined reference to `add'
[build] collect2.exe: error: ld returned 1 exit status
[build] mingw32-make[2]: *** [CMakeFiles\test-c.dir\build.make:102: Debug/bin/test-c.exe] Error 1
[build] mingw32-make[1]: *** [CMakeFiles\Makefile2:84: CMakeFiles/test-c.dir/all] Error 2
[build] mingw32-make[1]: *** Waiting for unfinished jobs....
[build] C:/Users/fredom/Program Files/MinGW64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\test-cpp.dir/objects.a(test-cpp.cpp.obj): in function `main':
[build] C:/Users/fredom/workspace/test/cc/import/test-cpp.cpp:8:(.text+0x31): undefined reference to `sub(int, int)'
[build] collect2.exe: error: ld returned 1 exit status
[build] mingw32-make[2]: *** [CMakeFiles\test-cpp.dir\build.make:102: Debug/bin/test-cpp.exe] Error 1
[build] mingw32-make[1]: *** [CMakeFiles\Makefile2:110: CMakeFiles/test-cpp.dir/all] Error 2
[build] mingw32-make: *** [Makefile:135: all] Error 2
[proc] 命令“"C:\Users\fredom\Program Files\MinGW64\mingw64\bin\cmake.EXE" --build c:/Users/fredom/workspace/test/cc/import/build --config Debug --target install -j 22 --”已退出,代码为 2
[driver] 生成完毕: 00:00:00.589
[build] 生成已完成,退出代码为 2

根据输出来看,是编译错误了,并且错误发生在链接器 ld.exe 尝试为我们的目标在提供的导出库 calc 中寻找导出符号的阶段。可以看到 c 测试文件的错误是无法找到导出库的 cpp 动态库中的 add 函数定义符号,而 cpp 测试文件则是无法从提供的导出库中找到 sub(int, int) 函数定义符号。

使用 Windows 开发套件 VisualStudio 提供的 dumpbin 工具查看 dll 动态库文件中导出符号都有哪些。对 addsubcalc 三个库查看它们的导出符号表:

calc.dll 导出符号表:

File Type: DLL

  Section contains the following exports for libcalc.dll

    00000000 characteristics
    675948A3 time date stamp Wed Dec 11 16:09:07 2024
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 000015A0 _Z3addii
          2    1 000015C0 sub

add.dll 导出符号表:

File Type: DLL

  Section contains the following exports for libadd.dll

    00000000 characteristics
    67590BFE time date stamp Wed Dec 11 11:50:22 2024
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 000015A0 _Z3addii

sub.dll 导出符号表:

File Type: DLL

  Section contains the following exports for libsub.dll

    00000000 characteristics
    67590BFE time date stamp Wed Dec 11 11:50:22 2024
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 000015A0 sub

这里使用 MinGW64 编译出来的动态库,Windows 的 VS 编译工具链中的工具一样可以读出导出符号表。可以看到,使用 C++ 实现的 add 函数的编译产物中,函数符号是经过 C++ 编译器的 name mangling(名称修饰)的,而 C 实现的 sub 函数没有。这是因为 C++ 中有函数重载的概念,为了区分不同参数列表的同名函数,需要将参数列表的内容作为一种签名附带到函数名称上。

使用 extern C

如果我们在测试项目编译 C++ 源文件的时候告诉编译器,sub 这个函数是 C 实现的,不需要经过 C++ 编译中的函数名称修饰。因为 calc 库本身 sub 子模块还是 C 实现的,不能强求将库的实现从 C 迁移到 C++ ,一个更好的迁移方案是,对声明接口的头文件使用 extern "C" 预处理宏,结合 __cplusplus 这个编译器在编译 C++ 源文件时会自动携带的宏,可以配合完成“告诉编译器在动态库找这个接口的定义符号时,不要对函数进行名称修饰,尽管你在编译 C++ 源文件”。

所以,修改过后的 sub 函数的接口声明头文件内容改成:

#ifndef __SUB_H__
#define __SUB_H__

#ifdef __cplusplus
extern "C" {
#endif

int sub(int a, int b);

#ifdef __cplusplus
}
#endif

#endif

这样其他用户在引入这个库使用的时候,只要在 C++ 源文件中引入这个库, extern "C" 包含范围会启用,被包含的接口声明在编译期间查找提供的动态库中的导出符号表时,函数名称不经过编译器修饰。

现在测试项目的 CMake 构建阶段输出是:

[main] 正在生成文件夹: c:/Users/fredom/workspace/test/cc/import/build 
[build] 正在启动生成
[proc] 执行命令: "C:\Users\fredom\Program Files\MinGW64\mingw64\bin\cmake.EXE" --build c:/Users/fredom/workspace/test/cc/import/build --config Debug --target install -j 22 --
[build] [ 25%] Building C object CMakeFiles/test-c.dir/test-c.c.obj
[build] [ 50%] Building CXX object CMakeFiles/test-cpp.dir/test-cpp.cpp.obj
[build] [ 75%] Linking C executable Debug\bin\test-c.exe
[build] [100%] Linking CXX executable Debug\bin\test-cpp.exe
[build] C:/Users/fredom/Program Files/MinGW64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/14.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles\test-c.dir/objects.a(test-c.c.obj): in function `main':
[build] C:/Users/fredom/workspace/test/cc/import/test-c.c:8:(.text+0x96): undefined reference to `add'
[build] collect2.exe: error: ld returned 1 exit status
[build] mingw32-make[2]: *** [CMakeFiles\test-c.dir\build.make:102: Debug/bin/test-c.exe] Error 1
[build] mingw32-make[1]: *** [CMakeFiles\Makefile2:84: CMakeFiles/test-c.dir/all] Error 2
[build] mingw32-make[1]: *** Waiting for unfinished jobs....
[build] [100%] Built target test-cpp
[build] mingw32-make: *** [Makefile:135: all] Error 2
[proc] 命令“"C:\Users\fredom\Program Files\MinGW64\mingw64\bin\cmake.EXE" --build c:/Users/fredom/workspace/test/cc/import/build --config Debug --target install -j 22 --”已退出,代码为 2
[driver] 生成完毕: 00:00:00.823
[build] 生成已完成,退出代码为 2

可以看到 test-c.c 文件的编译还是错误,无法找到 add 函数的定义,但是 test-cpp.cpp 文件编译成功了,这是符合预期的,因为从刚才我们对三个动态库的导出符号表的检视来看,动态库中的 add 函数实际上名称是 _Z3addii ,编译器编译 C 源文件的时候,不会执行函数名称修饰,也自然就无法在提供的库中找到对应的导出符号,也就是说 C++ 源文件编译通过 extern "C 还是可以挽救一下兼容性调用 C 实现的函数的,但是反过来 C 就没法调用 C++ 实现的函数了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/934914.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MVC基础——市场管理系统(二)

文章目录 项目地址三、Produtcts的CRUD3.1 Products列表的展示页面(Read)3.1.1 给Product的Model里添加Category的属性3.1.2 View视图里展示Product List3.2 增加Product数据(Add)3.2.1 创建ViewModel用来组合多个Model3.2.2 在_ViewImposts里引入ViewModels3.2.3 添加Add的…

前端 mp4 视频改成 m3u8 流模式

前端 mp4 视频改成 m3u8 流模式 mp4 视频的问题 1、mp4 视频通常对应一个文件&#xff0c;播放时需要加载全部文件&#xff0c;消耗网络资源。如果用户从中间某个时间访问&#xff0c;也会从头开始下载&#xff0c;浪费服务器性能。 2、mp4 视频文件容易被用户下载到本地。有…

爬虫基础之代理的基本原理

在做爬虫的过程中经常会遇到一种情况&#xff0c;就是爬虫最初是正常运行、正常抓取数据的&#xff0c;一切看起来都是那么美好&#xff0c;然而一杯茶的工夫就出现了错误&#xff0c;例如 403 Forbidden&#xff0c;这时打开网页一看&#xff0c;可能会看到“您的IP访问频率太…

如何将自己的PHP类库发布到composer仓库

将自己的 PHP 类库发布到 Composer 仓库&#xff0c;需要经过一系列的准备和操作步骤&#xff0c;以下是详细说明&#xff1a; 准备工作 创建类库项目&#xff1a;确保你的 PHP 类库项目具有清晰的目录结构&#xff0c;遵循 PSR-4 等 PHP 编码规范。通常&#xff0c;类文件应…

频道web - 性能优化之往返缓存

性能优化之往返缓存 往返缓存简介:如何验证当前页面是否有往返缓存?有哪些开发场景可以用bfcache提升性能?哪些无需关注?阻止页面进行往返缓存的行为都有哪些?1、缓存2、强制刷新3、浏览器设置4、JavaScript 代码5、网络问题6、 iframe 本身不符合 bfcache 的条件为什么会…

当前热门 DApp 模式解析:六大方向的趋势与创新

去中心化应用&#xff08;DApp&#xff09;随着区块链技术的不断发展&#xff0c;已经成为 Web3 领域的核心创新之一。与传统应用不同&#xff0c;DApp 通过智能合约运行在区块链上&#xff0c;具有去中心化、透明、安全等特点。近年来&#xff0c;随着用户需求的变化和技术的发…

Windows中将springboot项目运行到docker的容器中

0&#xff0c;先打包好项目&#xff0c;再启动docker 1&#xff0c;在Java项目根目录下创建一个名为Dockerfile的文件&#xff08;没有扩展名&#xff09;&#xff0c;并添加以下内容。 # 使用OpenJDK的基础镜像 FROM openjdk:8-jdk-alpine# 设置工作目录 WORKDIR /app# 将项…

使用html 和javascript 实现微信界面功能1

1.功能说明&#xff1a; 搜索模块: 提供一个搜索框&#xff0c;但目前没有实现具体的搜索功能。 好友模块: 在左侧的“好友”部分有一个“查看好友”按钮。点击左侧的“查看好友”按钮时&#xff0c;会在右侧显示所有好友的列表。列表中每个好友可以点击查看详情&#xff0c;包…

uniapp——H5中使用富文本编辑器,如何使用。

一、插件市场 去插件市场找到这个插件https://ext.dcloud.net.cn/plugin?id14726 二、引入 找到自己项目引入 项目里面多了很多文件 三、使用 找到A页面&#xff0c;在里面引入组件 <view class"editBox"><sp-editor exportHtml"handleExpor…

前端视角下的Go语法学习:创建 Go 项目

今日话题 使用 GoLand 创建 Go 项目 作者&#xff1a; 时间&#xff1a;2024年6月20日 17时16分14秒 主线任务 一、GoLand 创建项目 1、点击 “new Project” 按钮 2、已经有下载过两个 Golang SDK 版本&#xff0c;选择版本创建即可~ 3、如果没有下载过Golang SDK&#…

使用pyinstaller打包pyqt的程序,运行后提示ModuleNotFoundError: No module named ‘Ui_main‘

环境&#xff1a;windowpython3.9pyqt6 使用pyqt UI编辑器生成了main.ui &#xff0c;main.ui编译成了Ui_main.py main.py 使用当前目录下的Ui_main.py。 打包过程没报错&#xff0c;运行报错。 错误如下: 解决方法&#xff1a;pyinstaller -Fw main.py --paths. 使…

1. 机器学习基本知识(4)——机器学习测试和验证

1.6 测试和验证 了解模型对新实例的泛化能力的唯一方法是在新实例上进行实际尝试。 一种方法是将模型部署到生产环境并监控其性能。 ​ 这种方法很有效&#xff0c;但如果模型非常糟糕&#xff0c;你的用户就会抱怨&#xff0c;所以这显然不是最好的方法。 更好的选择是将数…

Qwen 论文阅读记录

本文仅作自己初步熟悉大模型&#xff0c;梳理之用&#xff0c;慢慢会更改/增加/删除&#xff0c;部分细节尚未解释&#xff0c;希望不断学习之后&#xff0c;能够完善补充。若有同道之人&#xff0c;欢迎指正探讨。 关于后面的code-qwen and math-qwen&#xff0c;我个人认为依…

yarn 安装问题

Couldn’t find package “regenerator-runtime” on the “npm” registry. Error: Couldn’t find package “watch-size” on the “npm” regist 标题Error: Couldn’t find package “babel-helper-vue-jsx-merge-props” on the “npm” registry. Error: Couldn’t f…

【开源】基于SpringBoot框架的音乐网站与分享平台(计算机毕业设计)+万字说明文档 T011

系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境&#xff1a; Tomcat 7.x,8.x,9.x版本均可 操作系统…

【SpringMVC】应用分层

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;场景引入 二&#xff1a;前后端分离三层架构 1&#xff1a;表现层 2&#xff1a;业务…

防火墙旁挂部署+故障切换

一、实验环境 华为ENSP 二、拓扑 三、目的 1、内网PC1访问Server 2、防火墙旁挂部署&#xff0c;对流量进行过滤&#xff0c;防火墙挂掉之后&#xff0c;内网PC1能继续访问到Server 3、防火墙恢复正常后&#xff0c;流量能回切至防火墙转发 四、思路&#xff1a; 1、AR1…

MySQL8版本升级

1.官方升级手册必看 1.1 理解升级过程会做什么 手册网址&#xff1a;https://dev.mysql.com/doc/refman/8.0/en/upgrading.html 升级mysql 系统数据库&#xff08;默认的库&#xff09;&#xff0c;升级mysql 用户数据库&#xff08;用户创建的库&#xff09; 升级步骤分为…

5G中的随机接入过程可以不用收RAR?

有朋友提到了一种不用接收RAR的RA过程&#xff0c;问这个是怎么回事。其实在刚刚写过的LTM cell switch篇章中就有提到&#xff0c;这里把所有相关的内容整理如下。 在RACH-less LTM场景&#xff0c;在进行LTM cell switch之前就要先知道target cell的TA信息&#xff0c;进而才…

QT数据库SQLite:QsqlTableModel使用总结

数据库连接、数据模型与界面组件所涉及的类之间的关系如下所示&#xff1a; 数据库类 QSqlDatabase 类用于建立与数据库的连接&#xff0c;QSqlDatabase 对象就表示这种连接。QSqlDatabase 类的功能主要分为三大部分&#xff1a; 1、创建数据库连接&#xff0c;即创建 QSqlDat…