Skip to content

C++ 源码库 ​

[🕒 预计 15 分钟] | 难度:入门

别人用 C++ 写好的函数(.h / .cpp 源码文件),不需要先编译成 DLL,直接把源码放进项目、声明成中文命令就能调用——源码会和你的工程一起编译,补全、参数提示、类型检查全部自动生效。

1. 三种 C++ 接入方式怎么选 ​

方式适合场景前提
C++ 源码库(本页)拿得到源码,允许随项目一起分发只要 .h/.cpp 文件
DLL 命令声明只有编译好的 .dll,拿不到源码先编译出 DLL
.lbmod 模块跨项目复用、隐藏源码、分发给别人模块封装流程

2. 把源码放进项目 ​

  1. 在 解决方案资源管理器 中右键项目节点,选择 添加 C++ 源码…。
  2. 在文件对话框中选中别人给你的 .cpp / .h 文件(可多选)。
  3. 文件会被复制到项目目录的 cpp/ 文件夹下(重名自动加 -2、-3 后缀),并自动打开「C++ 源码库」声明文件。

也可以用命令面板执行 项目:添加 C++ 源码…,或手动把源码文件放进项目源码目录。

3. 声明中文命令 ​

在声明文件 项目C++源码库.lcpp 中(项目节点的「C++ 源码库」按钮可随时打开),按下面的格式声明:

text
包 项目C++源码库

C++源码库 数学库
  源码 = cpp/mymath.cpp
  头文件 = cpp/mymath.h
  整数型 加法计算(整数型 a, 整数型 b) = add
    备注: 计算两数之和
  小数型 平均值(小数型 总和, 整数型 个数) = average
结束C++源码库
  • 源码 =:参与编译的 .cpp 文件(相对项目源码目录),必填,可写多行。
  • 头文件 =:可选。写了就以你的头文件里的声明为准;不写则 IDE 按「类型映射表」自动生成期望签名。
  • = add:源码里的 C++ 函数名(英文),必须填写;左边的中文命令名由你起。

声明完成后,在窗口代码里直接调用:

text
局部 整数型 和值 = 加法计算(123, 456)

4. C++ 侧要怎么写 ​

C++ 函数必须是全局自由函数,签名与声明一致:

中文类型C++ 参数写法C++ 返回写法
整数型intint
长整数型long longlong long
小数型 / 单精度小数型double / float同左
逻辑型boolbool
字节型unsigned char同左
文本型const wchar_t*(或 std::wstring 值传递)const wchar_t*
字节集const std::vector<unsigned char>&按值 std::vector<unsigned char>
指针整数uintptr_tuintptr_t
传址参数指针,如 int*(调用端自动传 &变量)—
cpp
// cpp/mymath.cpp
#include "mymath.h"

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

const wchar_t* greet(const wchar_t* name) {
    static std::wstring message;
    message = std::wstring(L"你好,") + (name ? name : L"");
    return message.c_str();
}

5. 传函数(回调) ​

命令参数可以声明处理器并带内联签名,把中文子程序直接传给 C++:

text
  空 执行进度(整数型 次数, 处理器 进度回调(整数型 序号, 文本型 阶段)) = run_progress

C++ 侧形参写 std::function<void(int, const wchar_t*)>(回调返回固定为空):

cpp
void run_progress(int count, std::function<void(int, const wchar_t*)> callback) {
    for (int i = 1; i <= count; ++i) callback(i, L"步");
}

调用时传 &子程序名(子程序写在窗口类里,签名必须与内联签名一致,IDE 会强校验):

text
回调计数 = 0
执行进度(5, &进度回调)

注意:默认情况下回调在与调用相同的线程里直接执行(线程透传),不要在回调外长期保存它。

5.1 跨线程封送(界面线程) ​

C++ 侧要在工作线程里触发回调(后台下载进度、线程池任务等)时,给处理器参数加 界面线程 后缀:

text
  空 执行线程任务(整数型 次数, 处理器 线程进度(整数型 序号, 文本型 阶段) 界面线程) = run_worker

这样回调被工作线程触发时,参数会以值拷贝快照经 PostMessage 投递到界面线程执行——处理器里可以放心操作控件。语义要点:

  • 必须显式写 界面线程 才封送,不写就维持线程透传(封送把同步调用变成了异步投递,不允许悄悄发生);
  • 封送回调不能带 回传 类型(异步拿不回返回值,两者同写会在声明时报错);
  • 同一线程的多次投递按顺序执行,跨线程不保证顺序;
  • 处理器里不要阻塞,投递方也不要等待——封送是「发了就走」(fire-and-forget)。

6. 类(句柄化) ​

用类块把 C++ 类包装成「创建 / 销毁 / 方法」命令,句柄是长整数型:

text
C++源码库 计数器库
  头文件 = cpp/counter.h
  类 计数器 = Counter
    方法 计数器 创建(整数型 初始值)
    方法 空 销毁()
    方法 整数型 取当前() = get_value
    方法 空 累加(整数型 步长) = add_value
  结束类
结束C++源码库

调用:

text
局部 长整数型 对象 = 0
局部 整数型 当前值 = 0
对象 = 计数器_创建(40)
计数器_累加(对象, 2)
当前值 = 计数器_取当前(对象)
计数器_销毁(对象)

要点:类定义必须来自同库声明的「头文件 =」(可以全部内联在头文件里);「创建」「销毁」是保留名(分别生成 new / delete);句柄变量可以直接用类名声明(局部 计数器 对象 = 0),也可以写 长整数型;句柄是裸指针,复制句柄等于共享对象,用完必须 销毁;多步句柄流程要先声明(不带初始值)再按序赋值——带初始值的局部声明会被提升到子程序最前,导致后面的初始化器抢在中间步骤之前执行。

7. 进阶能力(v3) ​

回调返回值:处理器 校验器(整数型 值) 回传 逻辑型 —— C++ 侧收 std::function<bool(int)>,中文子程序返回逻辑型即可(缺省返回为空)。

一维数组:整数型[] 求和(整数型[] 数据) —— C++ 侧参数写 const std::vector<int>&、返回按值 std::vector<int>;文本型数组的元素是 std::wstring。数组返回值建议就地嵌套消费(如 校验缩放(缩放数组(源数组(), 3)))。

附加包含目录:包含目录 = cpp/third_party —— 整个目录会随源码一起复制,.cpp 里写相对路径 #include "third_party/xxx.h" 即可命中。

异常捕获:库内写一行 异常 = 捕获 —— 本库全部 C++ 函数经 try/catch 包装,抛异常不再让程序崩溃,而是返回零值;用自动生成的 库名_取错误() 读取异常文本(读取后清空,没有异常时为空文本)。

8. 命名空间 ​

C++ 函数在命名空间里?库内写一行 命名空间 = xxx(整个库的函数必须在同一命名空间):

text
C++源码库 扩展库
  源码 = cpp/ext.cpp
  命名空间 = lbv4
  整数型 三数相加(整数型 a, 整数型 b, 整数型 c) = plus3
结束C++源码库

.lcpp 调用写法完全不变(IDE 自动生成全局转发函数)。注意:只作用于函数命令;类块不支持命名空间(类定义来自你的头文件)。

9. 结构体(把 C++ 结构体当数据包传) ​

库内用结构体块声明一个结构体,字段逐行写(// 备注 可选):

text
C++源码库 几何库
  源码 = cpp/geometry.cpp
  头文件 = cpp/geometry.h
  结构体 点 = Point
    小数型 x // 横坐标
    小数型 y
    文本型 名称
  结束结构体
  小数型 两点距离(点 a, 点 b) = distance
  点 取中点(点 a, 点 b) = midpoint
结束C++源码库

IDE 会自动合成一整套命令:点_创建()(造一个全零的点并返回句柄)、点_销毁(句柄),以及每个字段的 点_取X(句柄) / 点_置X(句柄, 值)。命令的参数和返回都可以直接写结构体名。

C++ 侧怎么写:

  • 参数写 const Point&(IDE 生成的期望签名就是这个形态;写按值 Point 是另一个函数签名,会链接失败);
  • 返回按值写 Point(中文侧拿到的是自动包好的句柄,用完要 点_销毁);
  • 文本型字段就是 std::wstring——和 DLL 结构体不同,这里不需要定长数组,直接写普通结构体即可。
cpp
// cpp/geometry.h
#pragma once
#include <string>
struct Point {
    double x;
    double y;
    std::wstring 名称;
};
double distance(const Point& a, const Point& b);
Point midpoint(const Point& a, const Point& b);

中文侧调用(句柄流程记住「先声明、再按序赋值」):

text
局部 点 a = 0
局部 点 b = 0
局部 点 中点 = 0
a = 点_创建()
b = 点_创建()
点_置x(a, 3)
点_置y(a, 4)
中点 = 取中点(a, b)
调试输出(点_取x(中点))
点_销毁(中点)
点_销毁(b)
点_销毁(a)

结构体定义放哪? 库写了「头文件 =」就以你头文件里的 struct 为准(字段行只负责生成取/置命令);不写头文件时 IDE 会按字段行直接生成 struct 定义,你的 .cpp 里写 #include "CppSourceLibraries.h" 就能拿到它。

边界:结构体只在本库内可用;字段不支持数组、嵌套结构体和字节集;不支持与命名空间同库使用;类方法的参数/返回暂不支持结构体(用普通函数命令传)。

10. 注意事项(红线) ​

  • 只支持普通全局函数:模板、重载、类成员函数暂不支持(声明时会给出中文提示)。
  • 源码文件要保存为 UTF-8 编码(否则构建时中文注释会导致报错)。
  • 源码里不要定义 main、wWinMain、DllMain 等入口函数。
  • 函数内不要抛 C++ 异常。
  • 要隐藏源码或分发给别的项目复用时,请改用 DLL 命令声明或模块封装。