반응형

 

이 예제는 gdbus-codegen을 통해 생성된 c파일 그리고 header파일을 기존 cpp파일과 같이 빌드하는 과정을 나타낸다.

 

프로젝트 구조

project/
├── CMakeLists.txt
├── example.xml
├── server.cpp
└── client.cpp

 

exmaple.xml

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
  <interface name="org.example.Calculator">
    <method name="Add">
      <arg type="i" name="a" direction="in"/>
      <arg type="i" name="b" direction="in"/>
      <arg type="i" name="result" direction="out"/>
    </method>
    <method name="Sub">
      <arg type="i" name="a" direction="in"/>
      <arg type="i" name="b" direction="in"/>
      <arg type="i" name="result" direction="out"/>
    </method>
    <signal name="CalculationDone">
      <arg type="s" name="operation"/>
      <arg type="i" name="result"/>
    </signal>
  </interface>
</node>

 

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(dbus_example C CXX)

find_package(PkgConfig REQUIRED)
pkg_check_modules(GLIB REQUIRED glib-2.0 gio-2.0)
pkg_check_modules(GIO-UNIX gio-unix-2.0 REQUIRED)

find_program(GDBUS_CODEGEN gdbus-codegen)
if(NOT GDBUS_CODEGEN)
    message(FATAL_ERROR "gdbus-codegen not found!")
endif()

set(DBUS_XML ${CMAKE_CURRENT_SOURCE_DIR}/example.xml)

# XML 파일 존재 확인
if(NOT EXISTS ${DBUS_XML})
    message(FATAL_ERROR "example.xml not found at: ${DBUS_XML}")
endif()

message(STATUS "Using D-Bus XML: ${DBUS_XML}")

set(GENERATED_HEADER ${CMAKE_CURRENT_BINARY_DIR}/calculator-generated.h)
set(GENERATED_SOURCE ${CMAKE_CURRENT_BINARY_DIR}/calculator-generated.c)

# --generate-c-code로 헤더와 소스 동시 생성
add_custom_command(
    OUTPUT ${GENERATED_HEADER} ${GENERATED_SOURCE}
    COMMAND ${GDBUS_CODEGEN}
        --interface-prefix org.example.
        --generate-c-code ${CMAKE_CURRENT_BINARY_DIR}/calculator-generated
        ${DBUS_XML}
    DEPENDS ${DBUS_XML}
    COMMENT "Generating D-Bus code with --generate-c-code"
    VERBATIM
)

# 생성 타겟
add_custom_target(generate_dbus_code
    DEPENDS ${GENERATED_HEADER} ${GENERATED_SOURCE}
)

# 생성된 C 파일을 C 언어로 컴파일
set_source_files_properties(${GENERATED_SOURCE} PROPERTIES LANGUAGE C)

# 서버 실행 파일
add_executable(calculator_server
    server.cpp
    ${GENERATED_SOURCE}
)

add_dependencies(calculator_server generate_dbus_code)

target_include_directories(calculator_server PRIVATE
    ${GLIB_INCLUDE_DIRS}
    ${GIO-UNIX_INCLUDE_DIRS}
    ${CMAKE_CURRENT_BINARY_DIR}
)

target_link_libraries(calculator_server
    ${GLIB_LIBRARIES}
    ${GIO-UNIX_LIBRARIES} #생략 가능, GLIB_LIBARIES에 이미 GIO관련 라이브러리들이 포함되어있을 수 있기때문
)

# 클라이언트 실행 파일
add_executable(calculator_client
    client.cpp
    ${GENERATED_SOURCE}
)

add_dependencies(calculator_client generate_dbus_code)

target_include_directories(calculator_client PRIVATE
    ${GLIB_INCLUDE_DIRS}
    ${GIO-UNIX_INCLUDE_DIRS}
    ${CMAKE_CURRENT_BINARY_DIR}
)

target_link_libraries(calculator_client
    ${GLIB_LIBRARIES}
    ${GIO-UNIX_LIBRARIES} #생략 가능, GLIB_LIBARIES에 이미 GIO관련 라이브러리들이 포함되어있을 수 있기때문
)

 

server.cpp

#include <glib.h>
#include <gio/gio.h>

extern "C" {
    #include "calculator-generated.h"
}

static Calculator *calculator_skeleton = nullptr;

// 이 블록의 extern "C"는 생략 가능하다
extern "C" {
    static gboolean
    on_handle_add(Calculator *object,
                  GDBusMethodInvocation *invocation,
                  gint a, gint b,
                  gpointer user_data)
    {
        gint result = a + b;
        calculator_complete_add(object, invocation, result);
        
        gchar *msg = g_strdup_printf("Add(%d, %d)", a, b);
        calculator_emit_calculation_done(object, msg, result);
        g_free(msg);
        
        g_print("Add: %d + %d = %d\n", a, b, result);
        return TRUE;
    }

    static gboolean
    on_handle_sub(Calculator *object,
                  GDBusMethodInvocation *invocation,
                  gint a, gint b,
                  gpointer user_data)
    {
        gint result = a - b;
        calculator_complete_sub(object, invocation, result);
        
        gchar *msg = g_strdup_printf("Sub(%d, %d)", a, b);
        calculator_emit_calculation_done(object, msg, result);
        g_free(msg);
        
        g_print("Sub: %d - %d = %d\n", a, b, result);
        return TRUE;
    }

    static void
    on_bus_acquired(GDBusConnection *connection,
                    const gchar *name,
                    gpointer user_data)
    {
        GError *error = nullptr;
        
        calculator_skeleton = calculator_skeleton_new();
        
        g_signal_connect(calculator_skeleton, "handle-add",
                         G_CALLBACK(on_handle_add), nullptr);
        g_signal_connect(calculator_skeleton, "handle-sub",
                         G_CALLBACK(on_handle_sub), nullptr);
        
        if (!g_dbus_interface_skeleton_export(
                G_DBUS_INTERFACE_SKELETON(calculator_skeleton),
                connection,
                "/org/example/Calculator",
                &error))
        {
            g_printerr("Failed to export object: %s\n", error->message);
            g_error_free(error);
        }
        else
        {
            g_print("Calculator service ready\n");
        }
    }

    static void
    on_name_acquired(GDBusConnection *connection,
                     const gchar *name,
                     gpointer user_data)
    {
        g_print("Name acquired: %s\n", name);
    }

    static void
    on_name_lost(GDBusConnection *connection,
                 const gchar *name,
                 gpointer user_data)
    {
        g_printerr("Name lost: %s\n", name);
    }
}

int main(void)
{
    GMainLoop *loop;
    guint owner_id;
    
    loop = g_main_loop_new(nullptr, FALSE);
    
    owner_id = g_bus_own_name(G_BUS_TYPE_SESSION,
                               "org.example.Calculator",
                               G_BUS_NAME_OWNER_FLAGS_NONE,
                               on_bus_acquired,
                               on_name_acquired,
                               on_name_lost,
                               nullptr, nullptr);
    
    g_main_loop_run(loop);
    
    g_bus_unown_name(owner_id);
    g_main_loop_unref(loop);
    if (calculator_skeleton)
        g_object_unref(calculator_skeleton);
    
    return 0;
}

 

 

client.cpp

#include <glib.h>
#include <gio/gio.h>

extern "C" {
    #include "calculator-generated.h"
}

// 이 블록의 extern "C"는 생략 가능하다
extern "C" {
    static void
    on_calculation_done(Calculator *proxy,
                        const gchar *operation,
                        gint result,
                        gpointer user_data)
    {
        g_print("Signal received: %s = %d\n", operation, result);
    }
}

int main(void)
{
    GError *error = nullptr;
    Calculator *proxy;
    gint result;
    
    proxy = calculator_proxy_new_for_bus_sync(
        G_BUS_TYPE_SESSION,
        G_DBUS_PROXY_FLAGS_NONE,
        "org.example.Calculator",
        "/org/example/Calculator",
        nullptr, &error);
    
    if (error)
    {
        g_printerr("Failed to create proxy: %s\n", error->message);
        g_error_free(error);
        return 1;
    }
    
    g_signal_connect(proxy, "calculation-done",
                     G_CALLBACK(on_calculation_done), nullptr);
    
    if (calculator_call_add_sync(proxy, 10, 5, &result, nullptr, &error))
    {
        g_print("Add result: %d\n", result);
    }
    else
    {
        g_printerr("Add failed: %s\n", error->message);
        g_error_free(error);
    }
    
    if (calculator_call_sub_sync(proxy, 10, 5, &result, nullptr, &error))
    {
        g_print("Sub result: %d\n", result);
    }
    else
    {
        g_printerr("Sub failed: %s\n", error->message);
        g_error_free(error);
    }
    
    g_print("Waiting for signals (Press Ctrl+C to exit)...\n");
    GMainLoop *loop = g_main_loop_new(nullptr, FALSE);
    g_main_loop_run(loop);
    
    g_object_unref(proxy);
    g_main_loop_unref(loop);
    
    return 0;
}

 

 

빌드

rm -rf build
mkdir build
cd build
cmake ..
make VERBOSE=1

 

 

실행화면

서버측:

Calculator service ready
Name acquired: org.example.Calculator

Add: 10 + 5 = 15
Sub: 10 - 5 = 5

 

 

클라이언트측:

Add result: 15
Sub result: 5
Waiting for signals (Press Ctrl+C to exit)...
Signal received: Add(10, 5) = 15
Signal received: Sub(10, 5) = 5

 

 

핵심 변경사항:

  1. project(dbus_example C CXX) - C와 C++ 모두 활성화  (CMakeLists.txt 파일 안에서)
  2. 헤더파일을 포함할 때 extern "C" {} 블록 안에 배치

 

반응형