如何封装和定义一个函数

本文以车企嵌入式软件开发为背景,系统介绍函数封装与定义的最佳实践。


一、函数设计的核心原则

1.1 单一职责原则 (Single Responsibility Principle)

原则:一个函数只做一件事,并把它做好。

cpp 复制代码
// ❌ 错误示例:一个函数做太多事
int ProcessEngineData(TEngineData *pData)
{
    // 1. 验证数据
    if (NULL == pData) return ERR_NULL_PTR;
    if (pData->rpm < 0) return ERR_INVALID_RPM;
    
    // 2. 计算油耗
    double fuelRate = pData->rpm * 0.001 + pData->throttle * 0.05;
    
    // 3. 更新仪表盘
    UpdateDashboard(pData->rpm, pData->speed);
    
    // 4. 记录日志
    LogEngineStatus(pData);
    
    // 5. 发送CAN报文
    SendCanFrame(CAN_ID_ENGINE_STATUS, pData, sizeof(TEngineData));
    
    return SYS_RET_OK;
}

// ✅ 正确示例:拆分为多个单一职责函数
int ValidateEngineData(const TEngineData *pData)
{
    if (NULL == pData)
    {
        return ERR_NULL_PTR;
    }
    if (pData->rpm < 0 || pData->rpm > MAX_RPM)
    {
        return ERR_INVALID_RPM;
    }
    return SYS_RET_OK;
}

double CalculateFuelConsumption(int rpm, int throttlePercent)
{
    return rpm * FUEL_FACTOR_RPM + throttlePercent * FUEL_FACTOR_THROTTLE;
}

void NotifyDashboard(const TEngineData *pData)
{
    UpdateDashboard(pData->rpm, pData->speed);
}

int BroadcastEngineStatus(const TEngineData *pData)
{
    return SendCanFrame(CAN_ID_ENGINE_STATUS, pData, sizeof(TEngineData));
}

1.2 抽象层次一致性

原则:函数内的所有操作应处于同一抽象层次。

cpp 复制代码
// ❌ 错误示例:抽象层次混乱
int StartVehicle(TVehicleContext *pCtx)
{
    // 高层操作
    if (!AuthenticateKey(pCtx->keyId))
    {
        return ERR_AUTH_FAILED;
    }
    
    // 低层细节(不应在此出现)
    unsigned char canData[8] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    write(pCtx->canFd, canData, 8);
    
    // 高层操作
    InitializeEcu(pCtx);
    
    return SYS_RET_OK;
}

// ✅ 正确示例:保持抽象层次一致
int StartVehicle(TVehicleContext *pCtx)
{
    int ret = SYS_RET_OK;
    
    ret = AuthenticateKey(pCtx->keyId);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    ret = WakeUpCanNetwork(pCtx);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    ret = InitializeAllEcus(pCtx);
    
    return ret;
}

// 低层细节封装在独立函数中
int WakeUpCanNetwork(TVehicleContext *pCtx)
{
    TCanFrame wakeupFrame;
    wakeupFrame.canId = CAN_ID_WAKEUP;
    wakeupFrame.dlc = 1;
    wakeupFrame.data[0] = CMD_NETWORK_WAKEUP;
    
    return SendCanFrame(pCtx->canFd, &wakeupFrame);
}

1.3 最小知识原则 (Law of Demeter)

原则:函数只应与直接相关的对象交互,避免"链式调用"。

cpp 复制代码
// ❌ 错误示例:过度了解内部结构
int GetCurrentSpeed(TVehicle *pVehicle)
{
    return pVehicle->pChassis->pWheelSensor->pEncoder->currentSpeed;
}

// ✅ 正确示例:通过接口逐层获取
int GetCurrentSpeed(TVehicle *pVehicle)
{
    if (NULL == pVehicle || NULL == pVehicle->pChassis)
    {
        return 0;
    }
    return Chassis_GetSpeed(pVehicle->pChassis);
}

// 底盘模块提供接口
int Chassis_GetSpeed(TChassis *pChassis)
{
    if (NULL == pChassis || NULL == pChassis->pWheelSensor)
    {
        return 0;
    }
    return WheelSensor_GetSpeed(pChassis->pWheelSensor);
}

二、函数签名设计

2.1 函数命名规范

规范:动词 + 宾语,帕斯卡命名法

操作类型 推荐前缀 示例
获取值 Get GetEngineRpm(), GetBatteryVoltage()
设置值 Set SetThrottlePosition(), SetBrakeForce()
检查状态 Is/Has/Can IsEngineRunning(), HasDtcFault(), CanShiftGear()
执行动作 动词 StartEngine(), ApplyBrake(), ShiftToGear()
计算值 Calculate/Compute CalculateFuelConsumption(), ComputeSteeringAngle()
初始化 Init/Initialize InitCanController(), InitializeSensorArray()
转换 Convert/Parse ConvertRpmToSpeed(), ParseDtcCode()
验证 Validate/Check ValidateCanFrame(), CheckSensorRange()
cpp 复制代码
// ✅ 命名示例
int Engine_GetRpm(TEngineContext *pCtx);
int Engine_SetThrottlePercent(TEngineContext *pCtx, int percent);
bool Engine_IsRunning(TEngineContext *pCtx);
int Engine_Start(TEngineContext *pCtx);
double Engine_CalculateTorque(int rpm, int throttle);

2.2 参数设计原则

原则一:参数数量控制在 3-4 个以内
cpp 复制代码
// ❌ 错误示例:参数过多
int ConfigureEngine(int cylinders, int displacement, int maxRpm, 
                    int idleRpm, double compressionRatio, 
                    int fuelType, int ignitionTiming, int valveTiming)
{
    // ...
}

// ✅ 正确示例:使用结构体封装
typedef struct
{
    int cylinders;
    int displacement;
    int maxRpm;
    int idleRpm;
    double compressionRatio;
    int fuelType;
    int ignitionTiming;
    int valveTiming;
} TEngineConfig;

int ConfigureEngine(TEngineContext *pCtx, const TEngineConfig *pConfig)
{
    // ...
}
原则二:输入参数在前,输出参数在后
cpp 复制代码
// ✅ 正确示例
int CalculateGearRatio(int inputRpm,      // 输入:输入转速
                       int outputRpm,     // 输入:输出转速
                       double *pRatio);   // 输出:齿轮比

int ReadSensorData(int sensorId,          // 输入:传感器ID
                   TSensorData *pData);   // 输出:传感器数据
原则三:使用 const 修饰只读参数
cpp 复制代码
// ✅ 正确示例:明确参数意图
int ProcessCanFrame(const TCanFrame *pFrame,    // 只读输入
                    TProcessResult *pResult);   // 可写输出

void LogDtcFault(const TDtcInfo *pInfo);        // 只读输入
原则四:指针参数需进行有效性检查
cpp 复制代码
int Engine_SetConfig(TEngineContext *pCtx, const TEngineConfig *pConfig)
{
    // 参数有效性检查
    if (NULL == pCtx)
    {
        return ERR_NULL_CONTEXT;
    }
    if (NULL == pConfig)
    {
        return ERR_NULL_CONFIG;
    }
    
    // 业务逻辑
    pCtx->config = *pConfig;
    
    return SYS_RET_OK;
}

2.3 返回值设计

模式一:返回错误码 + 输出参数
cpp 复制代码
// 适用于需要返回复杂数据的场景
int Sensor_ReadTemperature(int sensorId, double *pTemperature)
{
    if (NULL == pTemperature)
    {
        return ERR_NULL_PTR;
    }
    
    if (sensorId < 0 || sensorId >= MAX_SENSOR_COUNT)
    {
        return ERR_INVALID_SENSOR_ID;
    }
    
    *pTemperature = ReadAdcValue(sensorId) * TEMP_SCALE_FACTOR;
    
    return SYS_RET_OK;
}

// 调用方式
double temperature;
int ret = Sensor_ReadTemperature(SENSOR_COOLANT, &temperature);
if (ret == SYS_RET_OK)
{
    ProcessTemperature(temperature);
}
模式二:直接返回结果值
cpp 复制代码
// 适用于简单计算或获取操作
int Engine_GetRpm(TEngineContext *pCtx)
{
    if (NULL == pCtx)
    {
        return 0;  // 或定义特殊错误值
    }
    return pCtx->currentRpm;
}

bool Engine_IsOverheating(TEngineContext *pCtx)
{
    if (NULL == pCtx)
    {
        return false;
    }
    return pCtx->coolantTemp > OVERHEAT_THRESHOLD;
}

// 调用方式
int rpm = Engine_GetRpm(pEngine);
if (Engine_IsOverheating(pEngine))
{
    ActivateCoolingFan();
}
模式三:统一错误码定义
cpp 复制代码
// 错误码定义(头文件)
#define SYS_RET_OK              0       // 成功
#define ERR_NULL_PTR           -1       // 空指针
#define ERR_INVALID_PARAM      -2       // 无效参数
#define ERR_TIMEOUT            -3       // 超时
#define ERR_BUSY               -4       // 设备忙
#define ERR_NOT_READY          -5       // 未就绪
#define ERR_CAN_TX_FAILED      -100     // CAN发送失败
#define ERR_CAN_RX_TIMEOUT     -101     // CAN接收超时
#define ERR_SENSOR_OFFLINE     -200     // 传感器离线
#define ERR_SENSOR_FAULT       -201     // 传感器故障

// 错误码判断宏
#define IS_SUCCESS(ret)  ((ret) == SYS_RET_OK)
#define IS_FAILED(ret)   ((ret) != SYS_RET_OK)

三、函数封装模式

3.1 模块化封装(C语言风格)

模式:使用模块前缀 + 操作名,通过第一个参数传递上下文

cpp 复制代码
// ========== Engine.h ==========
#ifndef ENGINE_H_
#define ENGINE_H_

// 前向声明
typedef struct TEngineContext TEngineContext;

// 生命周期管理
int Engine_Create(TEngineContext **ppCtx);
int Engine_Destroy(TEngineContext *pCtx);
int Engine_Initialize(TEngineContext *pCtx, const TEngineConfig *pConfig);

// 状态查询
int Engine_GetRpm(TEngineContext *pCtx);
int Engine_GetState(TEngineContext *pCtx);
bool Engine_IsRunning(TEngineContext *pCtx);

// 控制操作
int Engine_Start(TEngineContext *pCtx);
int Engine_Stop(TEngineContext *pCtx);
int Engine_SetThrottle(TEngineContext *pCtx, int percent);

#endif /* ENGINE_H_ */

// ========== Engine.cpp ==========
#include "Engine.h"

// 私有结构体定义
struct TEngineContext
{
    EEngineState state;
    int currentRpm;
    int targetRpm;
    int throttlePercent;
    TEngineConfig config;
    ICanBus *pCanBus;
};

int Engine_Create(TEngineContext **ppCtx)
{
    if (NULL == ppCtx)
    {
        return ERR_NULL_PTR;
    }
    
    TEngineContext *pCtx = (TEngineContext *)malloc(sizeof(TEngineContext));
    if (NULL == pCtx)
    {
        return ERR_NO_MEMORY;
    }
    
    memset(pCtx, 0, sizeof(TEngineContext));
    pCtx->state = ENGINE_STATE_OFF;
    
    *ppCtx = pCtx;
    return SYS_RET_OK;
}

int Engine_Start(TEngineContext *pCtx)
{
    if (NULL == pCtx)
    {
        return ERR_NULL_PTR;
    }
    
    if (pCtx->state != ENGINE_STATE_OFF)
    {
        return ERR_INVALID_STATE;
    }
    
    // 发送启动命令
    TCanFrame startCmd;
    startCmd.canId = CAN_ID_ENGINE_CMD;
    startCmd.dlc = 2;
    startCmd.data[0] = CMD_ENGINE_START;
    startCmd.data[1] = 0x00;
    
    int ret = pCtx->pCanBus->Send(&startCmd);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    pCtx->state = ENGINE_STATE_CRANKING;
    
    return SYS_RET_OK;
}

3.2 面向对象封装(C++风格)

cpp 复制代码
// ========== IEngine.h ==========
class IEngine
{
public:
    virtual ~IEngine() {}
    
    virtual int Initialize(const TEngineConfig *pConfig) = 0;
    virtual int Start() = 0;
    virtual int Stop() = 0;
    virtual int GetRpm() const = 0;
    virtual bool IsRunning() const = 0;
};

// ========== Engine.h ==========
class Engine : public IEngine
{
public:
    Engine(ICanBus *pCanBus);
    virtual ~Engine();
    
    // IEngine 接口实现
    virtual int Initialize(const TEngineConfig *pConfig);
    virtual int Start();
    virtual int Stop();
    virtual int GetRpm() const;
    virtual bool IsRunning() const;
    
private:
    // 禁止拷贝
    Engine(const Engine &);
    Engine &operator=(const Engine &);
    
    // 私有辅助方法
    int SendEngineCommand(unsigned char cmd);
    int WaitForState(EEngineState targetState, int timeoutMs);
    
    // 成员变量
    ICanBus *m_pCanBus;
    EEngineState m_State;
    int m_CurrentRpm;
    TEngineConfig m_Config;
};

// ========== Engine.cpp ==========
Engine::Engine(ICanBus *pCanBus)
    : m_pCanBus(pCanBus)
    , m_State(ENGINE_STATE_OFF)
    , m_CurrentRpm(0)
{
    memset(&m_Config, 0, sizeof(m_Config));
}

int Engine::Start()
{
    if (m_State != ENGINE_STATE_OFF)
    {
        return ERR_INVALID_STATE;
    }
    
    int ret = SendEngineCommand(CMD_ENGINE_START);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    m_State = ENGINE_STATE_CRANKING;
    
    // 等待启动完成
    ret = WaitForState(ENGINE_STATE_RUNNING, START_TIMEOUT_MS);
    
    return ret;
}

int Engine::SendEngineCommand(unsigned char cmd)
{
    TCanFrame frame;
    frame.canId = CAN_ID_ENGINE_CMD;
    frame.dlc = 2;
    frame.data[0] = cmd;
    frame.data[1] = 0x00;
    
    return m_pCanBus->Send(&frame);
}

3.3 回调函数封装

cpp 复制代码
// 回调函数类型定义
typedef void (*PFN_OnDtcFault)(int dtcCode, int severity, void *pUserData);
typedef void (*PFN_OnCanFrameReceived)(const TCanFrame *pFrame, void *pUserData);

// 注册回调
typedef struct
{
    PFN_OnDtcFault pfnOnDtcFault;
    void *pDtcUserData;
    
    PFN_OnCanFrameReceived pfnOnCanReceived;
    void *pCanUserData;
} TEngineCallbacks;

int Engine_RegisterCallbacks(TEngineContext *pCtx, const TEngineCallbacks *pCallbacks)
{
    if (NULL == pCtx || NULL == pCallbacks)
    {
        return ERR_NULL_PTR;
    }
    
    pCtx->callbacks = *pCallbacks;
    
    return SYS_RET_OK;
}

// 内部触发回调
static void Engine_NotifyDtcFault(TEngineContext *pCtx, int dtcCode, int severity)
{
    if (NULL != pCtx->callbacks.pfnOnDtcFault)
    {
        pCtx->callbacks.pfnOnDtcFault(dtcCode, severity, pCtx->callbacks.pDtcUserData);
    }
}

// 使用示例
void MyDtcHandler(int dtcCode, int severity, void *pUserData)
{
    TMyApp *pApp = (TMyApp *)pUserData;
    Log_Error("DTC detected: 0x%04X, severity: %d", dtcCode, severity);
    Dashboard_ShowWarning(pApp->pDashboard, dtcCode);
}

int main()
{
    TEngineContext *pEngine = NULL;
    Engine_Create(&pEngine);
    
    TEngineCallbacks callbacks;
    memset(&callbacks, 0, sizeof(callbacks));
    callbacks.pfnOnDtcFault = MyDtcHandler;
    callbacks.pDtcUserData = &myApp;
    
    Engine_RegisterCallbacks(pEngine, &callbacks);
    
    // ...
}

四、函数实现技巧

4.1 早期返回(Early Return)

原则:先处理异常情况,减少嵌套层次。

cpp 复制代码
// ❌ 错误示例:嵌套过深
int ProcessBrakeInput(TBrakeContext *pCtx, int pressure)
{
    if (NULL != pCtx)
    {
        if (pressure >= 0 && pressure <= MAX_BRAKE_PRESSURE)
        {
            if (pCtx->state == BRAKE_STATE_READY)
            {
                // 业务逻辑...
                return SYS_RET_OK;
            }
            else
            {
                return ERR_NOT_READY;
            }
        }
        else
        {
            return ERR_INVALID_PRESSURE;
        }
    }
    else
    {
        return ERR_NULL_PTR;
    }
}

// ✅ 正确示例:早期返回
int ProcessBrakeInput(TBrakeContext *pCtx, int pressure)
{
    // 参数校验
    if (NULL == pCtx)
    {
        return ERR_NULL_PTR;
    }
    
    if (pressure < 0 || pressure > MAX_BRAKE_PRESSURE)
    {
        return ERR_INVALID_PRESSURE;
    }
    
    // 状态校验
    if (pCtx->state != BRAKE_STATE_READY)
    {
        return ERR_NOT_READY;
    }
    
    // 业务逻辑(无嵌套)
    pCtx->currentPressure = pressure;
    ApplyBrakeForce(pCtx, pressure);
    
    return SYS_RET_OK;
}

4.2 资源管理模式

模式一:创建-使用-销毁配对
cpp 复制代码
int ProcessCanLog(const char *logPath)
{
    FILE *pFile = NULL;
    TCanFrame *pFrameBuffer = NULL;
    int ret = SYS_RET_OK;
    
    // 资源分配
    pFile = fopen(logPath, "r");
    if (NULL == pFile)
    {
        ret = ERR_FILE_OPEN;
        goto cleanup;
    }
    
    pFrameBuffer = (TCanFrame *)malloc(FRAME_BUFFER_SIZE * sizeof(TCanFrame));
    if (NULL == pFrameBuffer)
    {
        ret = ERR_NO_MEMORY;
        goto cleanup;
    }
    
    // 业务逻辑
    ret = ParseCanFrames(pFile, pFrameBuffer, FRAME_BUFFER_SIZE);
    
cleanup:
    // 资源释放(逆序)
    if (NULL != pFrameBuffer)
    {
        free(pFrameBuffer);
    }
    if (NULL != pFile)
    {
        fclose(pFile);
    }
    
    return ret;
}
模式二:RAII (资源获取即初始化)(C++)
cpp 复制代码
class CanBusLock
{
public:
    explicit CanBusLock(ICanBus *pBus) : m_pBus(pBus), m_IsLocked(false)
    {
        if (NULL != m_pBus)
        {
            m_pBus->Lock();
            m_IsLocked = true;
        }
    }
    
    ~CanBusLock()
    {
        if (m_IsLocked && NULL != m_pBus)
        {
            m_pBus->Unlock();
        }
    }
    
private:
    ICanBus *m_pBus;
    bool m_IsLocked;
};

// 使用示例
int SendPriorityFrame(ICanBus *pBus, const TCanFrame *pFrame)
{
    CanBusLock lock(pBus);  // 构造时加锁
    
    // 业务逻辑
    return pBus->SendImmediate(pFrame);
    
}  // 析构时自动解锁,即使发生异常

4.3 函数长度控制

原则:一个函数的长度应控制在 30-50 行以内(不含空行和注释)。

cpp 复制代码
// ❌ 过长的函数应拆分
int ProcessVehicleState(TVehicleContext *pCtx)
{
    // 200+ 行的复杂逻辑...
}

// ✅ 拆分为多个小函数
int ProcessVehicleState(TVehicleContext *pCtx)
{
    int ret = SYS_RET_OK;
    
    // Step 1: 处理引擎状态
    ret = ProcessEngineState(pCtx);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    // Step 2: 处理底盘状态
    ret = ProcessChassisState(pCtx);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    // Step 3: 处理电气状态
    ret = ProcessElectricalState(pCtx);
    if (ret != SYS_RET_OK)
    {
        return ret;
    }
    
    // Step 4: 更新整车状态
    ret = UpdateOverallState(pCtx);
    
    return ret;
}

4.4 断言与防御性编程

cpp 复制代码
#include <assert.h>

// 开发阶段使用断言捕获编程错误
int Chassis_SetWheelSpeed(TChassisContext *pCtx, int wheelIndex, int speed)
{
    // 断言:捕获开发阶段的错误
    assert(NULL != pCtx);
    assert(wheelIndex >= 0 && wheelIndex < WHEEL_COUNT);
    
    // 防御性检查:处理运行时的异常情况
    if (speed < 0)
    {
        speed = 0;
    }
    if (speed > MAX_WHEEL_SPEED)
    {
        speed = MAX_WHEEL_SPEED;
    }
    
    pCtx->wheelSpeeds[wheelIndex] = speed;
    
    return SYS_RET_OK;
}

五、函数文档规范

5.1 函数注释模板

cpp 复制代码
/**
 * @brief  启动发动机
 * 
 * @details 执行发动机启动序列,包括:
 *          1. 检查启动条件(油压、电池电压等)
 *          2. 激活启动电机
 *          3. 等待发动机达到怠速转速
 * 
 * @param[in]  pCtx     发动机上下文指针,不可为 NULL
 * @param[in]  mode     启动模式:
 *                      - ENGINE_START_NORMAL: 正常启动
 *                      - ENGINE_START_COLD:   冷启动(延长预热)
 * 
 * @return 执行结果
 * @retval SYS_RET_OK        启动成功
 * @retval ERR_NULL_PTR      参数为空
 * @retval ERR_LOW_BATTERY   电池电压不足
 * @retval ERR_START_TIMEOUT 启动超时
 * 
 * @pre  发动机处于 ENGINE_STATE_OFF 状态
 * @post 成功后发动机处于 ENGINE_STATE_RUNNING 状态
 * 
 * @note 启动过程约需 2-5 秒,函数同步等待
 * @warning 连续启动失败超过 3 次将触发保护锁定
 * 
 * @see Engine_Stop()
 * @see Engine_GetState()
 */
int Engine_Start(TEngineContext *pCtx, EEngineStartMode mode);

5.2 简化注释(内部函数)

cpp 复制代码
// 计算制动力分配比例
// 输入:前后轴载荷比 (0.0-1.0)
// 返回:前轴制动力百分比 (0-100)
static int CalculateBrakeDistribution(double frontLoadRatio)
{
    // ...
}

六、常见错误与最佳实践

6.1 避免的错误模式

错误模式 问题 正确做法
过长函数 难以理解和维护 拆分为多个小函数
过多参数 调用繁琐,易出错 使用结构体封装
隐藏副作用 行为不可预测 函数名明确表达副作用
返回值被忽略 错误未处理 检查所有返回值
全局变量依赖 难以测试和复用 通过参数传递依赖
魔法数字 含义不明 使用命名常量

6.2 最佳实践清单

  • 函数名清晰表达其功能
  • 参数数量 ≤ 4 个
  • 所有指针参数进行 NULL 检查
  • 返回值有明确的错误定义
  • 函数长度 ≤ 50 行
  • 单一抽象层次
  • 无副作用或副作用在函数名中体现
  • 资源申请与释放配对
  • const 正确性
  • 关键函数有文档注释

七、总结

函数设计速查表

设计检查流程

复制代码
1. 这个函数只做一件事吗?
   └── 否 → 拆分为多个函数

2. 函数名能准确描述其行为吗?
   └── 否 → 重新命名

3. 参数数量是否过多?
   └── 是 → 使用结构体封装

4. 是否所有错误情况都处理了?
   └── 否 → 添加错误处理

5. 代码是否在同一抽象层次?
   └── 否 → 提取或内联函数

相关推荐
adamlevine72 小时前
【docker笔记-001】如何设置docker使得容器能在多个numa之间均匀使用内存
笔记·docker·容器·k8s·numa·numactl·k3s
1104.北光c°2 小时前
Leetcode3.无重复字符的最长子串 HashSet+HashMap 【hot100算法个人笔记】【java写法】
java·开发语言·笔记·程序人生·算法·leetcode·滑动窗口
weixin_307779132 小时前
2025年中国研究生数学建模竞赛C题:围岩裂隙精准识别与三维模型重构
c语言·数学建模·重构
鹭天2 小时前
目标检测学习笔记
笔记·学习·目标检测
MR.P_H_2 小时前
QT创建新工程,无法正常编译(Kit套件无法正常配置)
开发语言·qt
-许平安-2 小时前
MCP项目笔记五(PluginAPI)
c++·笔记·rpc·json·mcp·pluginapi
没有蛀牙lm2 小时前
windows下快速安装android studio(预估30min)
开发语言·javascript·webpack
无风听海2 小时前
LangGraph Thread 数据清理总结
java·开发语言·jvm·langchain·deep agents
JACK的服务器笔记2 小时前
Day12_网络吞吐基线测试
开发语言·网络·php