PAYATHON 2026

使用 MPLAB 和 MCC 为 PIC32CM 配置 C++ 工程后为何编译失败?

数据阿明

结论

现有日志表明,构建系统已尝试按 C++ 编译 main.cpp,但日志中没有足够的编译器诊断来确定根因。目前无法断定问题出在 MCC 配置、头文件兼容性还是 XC32 工具链。

入口代码虽然看起来像 C,但这本身不妨碍它按 C++ 编译。int main(void)、while (true) 和这些函数调用都可以出现在 C++ 程序中。注释里残留的 File Name: main.c 也不影响编译。

下一步先取得失败命令的完整输出,再根据具体错误处理。仅凭 FAILED: [code=2],无法确定可靠的修复参数。

日志已经确认了什么?

生成的 C 文件使用了:

xc32-gcc.exe ... -x c ...

main.cpp 对应的命令则包含:

-x c++

其输出目标也属于单独的 C++ 编译目标:

CMakeFiles/project_default_default_XC32_compile_cpp.dir/.../main.cpp.obj

因此,在贴出的构建命令中,main.cpp 被指定为按 C++ 编译。其他 .c 文件继续使用 -x c,符合 C/C++ 混合工程的组织方式,不需要把所有文件都改成 C++。

失败发生在生成 main.cpp.obj 的步骤。日志尚未显示最终链接命令,应先排查编译器调用及其诊断,暂时不能按链接错误处理。

怎样取得真正的错误信息?

先关闭并行构建,以便查看失败步骤前后的输出。在 PowerShell 中,可以使用原工程的构建目录执行:

& "C:/Program Files/CMake/bin/cmake.exe" --build "C:/projects/project/_build/project/default" --verbose --parallel 1

如果仍然只看到退出码,可以同时保存标准输出和错误输出:

& "C:/Program Files/CMake/bin/cmake.exe" --build "C:/projects/project/_build/project/default" --verbose --parallel 1 *> "build-cpp.log"

然后查看 build-cpp.log 中失败命令附近的内容,重点查找:

  • 带文件名、行号的语法或类型错误。
  • 无法找到或启动 C++ 编译组件的提示。
  • 不支持目标器件或某个命令行选项的提示。
  • 工具链安装或许可证相关的明确诊断。

这些都是尚待确认的可能原因,现有日志无法证明其中任何一种。

如果通过 CMake 仍然拿不到诊断,可以在原构建工作目录中单独执行日志里失败的编译命令,并保留全部参数。PowerShell 中执行带引号的程序路径时,需要在路径前加 &。

XC32-G~1.EXE 是日志中的 Windows 短文件名。排查时应核对它实际对应的可执行文件,不要只根据这个缩写判断工具是否正确。

MCC 生成 C++ 工程后,还需要检查哪些配置?

“Generate C++ Project”已触发入口文件切换,构建日志中也出现了 C++ 编译步骤。这说明部分配置已经生效,但还不能证明整条工具链、所有生成头文件和最终链接设置都已满足要求。

按以下顺序检查:

  1. 确认当前构建配置选中的 XC32 安装是预期版本,并核实该安装对目标器件的 C++ 支持。
  2. 确认 main.cpp 已加入当前目标,旧的 main.c 没有同时参与构建,避免后续出现重复的 main 定义。
  3. 保留 MCC 生成的 .c 文件按 C 编译,让应用侧的 .cpp 文件按 C++ 编译。
  4. 根据实际错误检查被 definitions.h 间接包含的头文件。C 编译通过,并不意味着所有声明和宏都符合 C++ 规则。
  5. 编译通过后,再核对最终链接步骤是否包含所需的 C++ 运行时支持。

不要直接把全部 -x c 改成 -x c++。这样会把生成的 C 驱动、启动文件等也交给 C++ 编译器,可能引入新的兼容性问题。

没有诊断信息时,也不要批量删除 -frtti、-fexceptions 等选项。只有错误明确指向某个选项,或已经确定应用不使用相关功能时,才有依据调整。

为什么 extern “C” 没有解决问题?

extern "C" 用于声明 C 语言链接,常用于让 C++ 代码调用由 C 编译器生成的函数。它不会把包裹的内容切换到 C 语言语法,也不能修复工具链启动失败、参数错误或头文件中的 C++ 语法问题。

先查看 definitions.h 及相关接口头文件是否已经包含:

#ifdef __cplusplus
extern "C" {
#endif

/* C API declarations */

#ifdef __cplusplus
}
#endif

如果已有这种保护,通常无需在调用端再包裹一次。如果没有,并且该头文件提供的是适合暴露为 C 接口的声明,可以在 C++ 调用端使用:

extern "C" {
#include "definitions.h"
}

不要把 C++ 类、模板或 C++ 标准库头文件一起放进这个块中。

原问题中已经尝试过包裹头文件,但没有成功。这不足以判断接口声明是否正确,也不能定位失败原因,仍需要查看编译器的具体诊断。

代码和目录应该怎样组织?

C++ 不要求专门的目录名称。需要检查的是源文件是否加入构建、头文件搜索路径是否正确,以及各源文件使用的语言和最终链接设置。

可以保留生成目录,在应用代码中逐步使用 C++。下面是一种组织示例,并非 MCC 强制要求:

My_MCC_Config/
├── mcc/
└── src/
    ├── config/
    ├── packs/
    ├── app/
    │   ├── application.cpp
    │   └── application.hpp
    └── main.cpp

cmake/
└── myprojectname/
    └── default/
        ├── .generated/
        ├── CMakeLists.txt
        └── CMakePresets.json

新增 application.cpp 后,还需要通过工程支持的方式将它加入构建目标。仅创建文件夹不能保证它被编译。修改构建设置时,也应确认相关文件是否会被工具重新生成和覆盖。

在工具链和头文件检查通过后,可以用一个简单的类验证应用侧 C++ 编译。假设 definitions.h 已正确提供 C++ 可调用的声明,并且编译模式支持 C++11 或更新标准:

#include "definitions.h"

class Application
{
public:
    void poll()
    {
        SYS_Tasks();
    }
};

int main(void)
{
    SYS_Initialize(nullptr);

    Application app;

    while (true)
    {
        app.poll();
    }
}

这个示例沿用生成代码的初始化和轮询流程,加入了一个简单的 C++ 类,可以用来验证工程配置,但不能保证绕过当前尚未明确的编译错误。

如果原始 main.cpp 都无法生成目标文件,应先根据该步骤的诊断解决问题。等最小工程编译、链接通过后,再逐步加入标准库、动态内存、异常或全局对象等功能,分别检查它们对运行时和启动流程的要求。

备注:内容仅供参考。