Skip to content

6.3 C++11에서 C99 인터페이스 연동

개요

C++11 프로젝트는 extern "C" 를 통해 C99 SDK 헤더와 import 라이브러리를 직접 호출할 수 있습니다. 이 방식의 핵심 가치는 인터페이스를 평면적으로 유지하고 의존성을 최소화하면서 ArmHandle* 기반의 C 스타일 수명 주기를 계속 사용하는 것입니다.

적용 시나리오

시나리오설명
최소 의존성C99 공개 헤더와 C/C++ 표준 라이브러리에만 의존
레거시 프로젝트프로젝트가 아직 C++11에 머물러 있어 C++17로 전환하기 어려움
플랫폼 호환성평면 ABI와 수동 수명 주기 관리가 필요

헤더 포함

cpp
extern "C" {  // C++11 프로젝트에서 C linkage 방식으로 C99 SDK 헤더를 포함합니다.
#include "c_arm_api.h"  // C99 SDK 최상위 헤더를 포함합니다.
}  // C linkage 선언을 종료합니다.

최소 연동 예제

예제 코드

cpp11/c99_connect_get_version/src/main.cpp
cpp
#include <stdio.h>

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

int main(void)
{
    // [ZH] 本示例直接在源码中写死连接地址,不解析命令行参数。
    // [EN] This example hard-codes the connection addresses in the source code and does not parse command-line arguments.
    ArmHandle* handle = Arm_Create();
    if (handle == NULL) {
        printf("[cpp11_c99_connect_get_version] 创建句柄失败 / Failed to create the handle\n");
        return 1;
    }

    // [ZH] 连接机器人。
    // [EN] Connect to the robot.
    const int connectRet = Arm_Connect(handle, "10.27.1.2", "10.27.1.102");
    if (connectRet != 0) {
        printf("[cpp11_c99_connect_get_version] 连接失败 / Connect failed, 状态码 / Status code: %d\n", connectRet);
        Arm_Destroy(handle);
        return 1;
    }
    printf("[cpp11_c99_connect_get_version] 机器人连接成功 / Robot connected successfully\n");

    // [ZH] 读取控制器版本。
    // [EN] Read the controller version.
    char version[128] = {0};
    const int versionRet = Arm_Info_GetControllerVersion(
        handle,
        version,
        sizeof(version)
    );
    if (versionRet != 0) {
        printf("[cpp11_c99_connect_get_version] 获取版本失败 / Get version failed, 状态码 / Status code: %d\n", versionRet);
        Arm_Disconnect(handle);
        Arm_Destroy(handle);
        return 1;
    }
    printf("[cpp11_c99_connect_get_version] 控制器版本 / Controller version: %s\n", version);

    // [ZH] 断开连接并销毁句柄。
    // [EN] Disconnect and destroy the handle.
    Arm_Disconnect(handle);
    Arm_Destroy(handle);
    printf("[cpp11_c99_connect_get_version] 示例结束 / Example finished\n");
    return 0;
}

CMake 구성

대응하는 전체 CMake 파일:

cpp11/c99_connect_get_version/CMakeLists.txt
txt
cmake_minimum_required(VERSION 3.20)
project(c99_connect_get_version LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
add_executable(c99_connect_get_version
    src/main.cpp
)
set_target_properties(c99_connect_get_version PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/output")
target_include_directories(c99_connect_get_version PRIVATE src dependency/include)
target_compile_options(c99_connect_get_version PRIVATE /utf-8)
target_link_libraries(c99_connect_get_version PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/dependency/lib/AgilebotCppSdk.lib")

빌드 및 실행

powershell
powershell -ExecutionPolicy Bypass -File example/cpp11/c99_connect_get_version/build/build.ps1

대응하는 빌드 스크립트:

cpp11/c99_connect_get_version/build/build.ps1
ps1
param()

chcp 65001 > $null
[Console]::OutputEncoding = [Text.Encoding]::UTF8

# 在示例脚本目录下执行,统一使用相对路径访问工程和产物。
Set-Location $PSScriptRoot
# 清理上一次拷贝的 SDK 依赖与示例运行产物,避免旧 DLL 干扰当前验证。
Remove-Item -Recurse -Force ..\dependency -ErrorAction Ignore
Remove-Item -Recurse -Force ..\output -ErrorAction Ignore
powershell -ExecutionPolicy Bypass -File ..\..\..\build\prepare_dependency.ps1 -Package c99
# 配置并编译当前独立示例工程。
cmake -S .. -B ..\buildcache -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_LINKER_TYPE=LLD

if (Test-Path ..\buildcache\compile_commands.json) {
    Copy-Item -Force ..\buildcache\compile_commands.json ..\compile_commands.json
}
cmake --build ..\buildcache
powershell -ExecutionPolicy Bypass -File ..\..\..\build\run_example.ps1 -Executable ..\output\c99_connect_get_version.exe

설명:

  • 빌드 스크립트는 output/release/c99-sdk/ 에서 의존성을 자동으로 준비합니다.
  • 실행 전에 장치를 변경해야 하는 경우 예제의 컨트롤러 주소와 티치 펜던트 주소를 수정합니다.

스레드 모델

C++11에서 C99 인터페이스로 SDK를 사용할 때 스레드 모델은 C99와 완전히 동일합니다.

  • 동기 호출은 호출 스레드에서 실행됩니다.
  • 주기 작업은 Arm_Info_AcquireAccess()Arm_Jogging_ContinuousMove() 처럼 세션 전용 네트워크 스레드에서 실행됩니다.
  • 구독 콜백은 WebSocket 메시지가 도착할 때 트리거됩니다.

자세한 내용은 1.3 스레드 모델을 참고하십시오.

C++17 SDK와 비교

특성C++11 + C99C++17 SDK
진입 형태extern "C" + ArmHandle*Arm 객체
반환 방식상태 코드 + out 파라미터STATUS_CODE / std::pair
리소스 관리수동 DestroyRAII 자동 해제
컨테이너 반환호출자가 배열을 직접 관리std::vector / std::string
적용 범위레거시 프로젝트, 최소 의존성 통합신규 프로젝트, 객체형 래퍼

예제 목록

example/cpp11 디렉터리는 example/c99 와 정렬된 예제 코드를 제공합니다.

예제설명
arm_connect_disconnectCore create/connect/disconnect/destroy
info_basic모든 Arm Info 인터페이스
alarm_query모든 Arm Alarm 인터페이스
motion_basic모든 Arm Motion 인터페이스
program_executionArm Program、BasScript、ArmProgramPose
registers_basic모든 Arm Registers 인터페이스
signals_basic모든 Arm Signals 인터페이스
trajectory_basicArm Trajectory 및 Arm RealTimeTrajectory
file_manager_basic모든 Arm FileManager 인터페이스
coordinate_system_basic모든 Arm CoordinateSystem 인터페이스
modbus_basic모든 Arm Modbus 인터페이스
jogging_basic모든 Arm Jogging 인터페이스
extension_basic모든 Arm Extension 인터페이스
sub_pub_basic모든 Arm SubPub 인터페이스
c99_connect_get_version최소 연동 폐루프 예제

주의 사항

  1. 헤더 파일은 반드시 extern "C" 안에 넣어야 합니다.
  2. ArmHandle*Arm_Destroy() 로 수동 해제해야 합니다.
  3. 모든 인터페이스의 반환값을 확인해야 합니다.
  4. 같은 ArmHandle* 를 여러 스레드에서 동시에 무분별하게 사용하지 마십시오.