DBUS
[DBUS] gdbus-codegen을 활용한 기본 예제 - 2. cpp파일 포함하여 빌드
2025. 11. 26. 00:40반응형
이 예제는 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
핵심 변경사항:
- project(dbus_example C CXX) - C와 C++ 모두 활성화 (CMakeLists.txt 파일 안에서)
- 헤더파일을 포함할 때 extern "C" {} 블록 안에 배치
반응형
