<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자를 위한 지침서</title>
    <link>https://i5i5.tistory.com/</link>
    <description>IT 개발자를 위한 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Mon, 1 Jun 2026 01:03:54 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>i5</managingEditor>
    <item>
      <title>[CMake] 플러그인 빌드 구조 만들기 - 예제 2. 정적라이브러리 심볼 활용</title>
      <link>https://i5i5.tistory.com/1650</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;실행파일 A가 플러그인 B를 동적로딩하고 이를 실행하는 예제를 살펴본다. 이 때, 각각 공통된 헤더(Log.h)를 사용하고 있고 이 Log.h의 구현부는 별도의 정적라이브러리(lib_src)로 실행파일 A 측에 구현되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;1. 프로젝트 구조&lt;/h2&gt;
&lt;pre id=&quot;code_1779031866877&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.
├── CMakeLists.txt
├── main.cpp              # 실행 파일 (A)
├── plugin.cpp            # 플러그인 (B)
├── include_main/         # 메인용 헤더 디렉토리
│   └── Log.h
├── include_plugin/       # 플러그인용 헤더 디렉토리 (내용은 동일)
│   └── Log.h
└── lib_src/              # 정적 라이브러리 소스
    └── Log.cpp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 소스코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Log.h (양쪽 폴더에 동일하게 복사)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779031146327&quot; class=&quot;css&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
class Log {
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void print();
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;lib_src/Log.cpp (정적 라이브러리 구현부)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779031146329&quot; class=&quot;arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Log.h&quot; // 컴파일 시점의 경로를 따름
#include &amp;lt;iostream&amp;gt;

void Log::print() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Main App's Static Lib] Hello from Log::print!&quot; &amp;lt;&amp;lt; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;main.cpp (실행 파일 A)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779031146331&quot; class=&quot;arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;dlfcn.h&amp;gt;
#include &quot;Log.h&quot; // include_main의 헤더 사용

int main() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Log mainLog;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mainLog.print(); // 메인에서 사용 (whole-archive 없이 포함되도록 함)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void* handle = dlopen(&quot;./libpluginB.so&quot;, RTLD_NOW);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!handle) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;Load fail: &quot; &amp;lt;&amp;lt; dlerror() &amp;lt;&amp;lt; std::endl;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto run = (void (*)())dlsym(handle, &quot;run_plugin&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (run) run();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dlclose(handle);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;plugin.cpp (공유 라이브러리 B)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779031146333&quot; class=&quot;arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Log.h&quot; // include_plugin의 헤더 사용 (경로가 다름)

extern &quot;C&quot; void run_plugin() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Log pluginLog;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 구현체가 없지만, 실행 시 메인 바이너리의 심볼을 찾아갑니다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pluginLog.print(); 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CMakeLists.txt&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;핵심은 --export-dynamic과 &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;17&quot;&gt;헤더 경로 설정&lt;/b&gt;이다.&lt;/p&gt;
&lt;pre id=&quot;code_1779031146333&quot; class=&quot;reasonml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.10)
project(CrossDirTest)

# 1. 정적 라이브러리 생성 (Main용 헤더 참조)
add_library(LogStatic STATIC lib_src/Log.cpp)
target_include_directories(LogStatic PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include_main)

# 2. 메인 실행 파일 생성
add_executable(AppA main.cpp)
target_link_libraries(AppA PRIVATE LogStatic ${CMAKE_DL_LIBS})

# [핵심] 메인 내부의 LogStatic 심볼을 플러그인이 볼 수 있게 공개
target_link_options(AppA PRIVATE &quot;-Wl,--export-dynamic&quot;)

# 3. 플러그인 생성 (Plugin 전용 별도 헤더 경로 참조)
add_library(pluginB SHARED plugin.cpp)
# 구현체(Log.cpp)를 링크하지 않고 헤더만 참조함
target_include_directories(pluginB PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include_plugin)
set_target_properties(pluginB PROPERTIES PREFIX &quot;lib&quot; OUTPUT_NAME &quot;pluginB&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 빌드하고 실행하면 다음과 같은 흐름이 발생한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot; data-path-to-node=&quot;22&quot;&gt;
&lt;li&gt;&lt;b data-path-to-node=&quot;22,0,0&quot; data-index-in-node=&quot;0&quot;&gt;빌드 단계&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;22,0,1&quot;&gt;
&lt;li&gt;AppA는 LogStatic을 링크하여 Log::print의 기계어 코드를 자기 몸체에 포함한다.&lt;/li&gt;
&lt;li&gt;pluginB는 Log::print가 어디 있는지 모르지만, &quot;나중에 누군가 채워주겠지&quot; 하고 구멍(Undefined Symbol)을 남겨둔 채 빌드된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-path-to-node=&quot;22,1,0&quot; data-index-in-node=&quot;0&quot;&gt;실행 단계 (./AppA)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot; data-path-to-node=&quot;22,1,1&quot;&gt;
&lt;li&gt;main()이 호출되어 첫 번째 &quot;Hello...&quot;를 출력한다.&lt;/li&gt;
&lt;li&gt;dlopen(&quot;./libpluginB.so&quot;)가 호출된다.&lt;/li&gt;
&lt;li&gt;시스템의 동적 링커(ld.so)가 pluginB를 로드하면서 빈 구멍(Log::print)을 채우려 한다.&lt;/li&gt;
&lt;li&gt;AppA가 --export-dynamic으로 빌드되었으므로, 링커는 AppA 내부에서 Log::print 심볼을 찾아 pluginB와 연결해 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 실행 결과&lt;/h3&gt;
&lt;div data-ved=&quot;0CAAQhtANahcKEwiq8Jb60MCUAxUAAAAAHQAAAAAQZA&quot; data-hveid=&quot;0&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;[Main App's Static Lib] Hello from Log::print!
[Main App's Static Lib] Hello from Log::print!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;결과적으로, 이처럼 &lt;b&gt;동일한 헤더 파일의 물리적 위치가 다르더라도&lt;/b&gt;, 클래스 이름과 함수 시그니처가 같으면 링커는 이를 동일한 대상으로 간주하고 성공적으로 연결한다. (--export-dynamic을 활용해야한다.)&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;단, 앞서 설명한 대로 한쪽 헤더에만 멤버 변수를 추가하는 등의 실수를 하면 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;26&quot;&gt;Segmentation Fault&lt;/b&gt;가 발생할 수 있으니 내용 일치에만 주의하시면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 그림&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용들을 간단하게 그림으로 나타내면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 빌드타임&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UPIPv/dJMcabc7WqJ/8N4z2uwejkLiLgtszITzT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UPIPv/dJMcabc7WqJ/8N4z2uwejkLiLgtszITzT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UPIPv/dJMcabc7WqJ/8N4z2uwejkLiLgtszITzT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUPIPv%2FdJMcabc7WqJ%2F8N4z2uwejkLiLgtszITzT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;531&quot; data-origin-width=&quot;692&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- 런타임&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;571&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q8MlD/dJMcaa6qi31/hikAZ89w1xxwL3rCmKWAXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q8MlD/dJMcaa6qi31/hikAZ89w1xxwL3rCmKWAXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q8MlD/dJMcaa6qi31/hikAZ89w1xxwL3rCmKWAXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ8MlD%2FdJMcaa6qi31%2FhikAZ89w1xxwL3rCmKWAXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;571&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;571&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Build(빌드)/CMake</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1650</guid>
      <comments>https://i5i5.tistory.com/1650#entry1650comment</comments>
      <pubDate>Mon, 18 May 2026 00:19:20 +0900</pubDate>
    </item>
    <item>
      <title>[CMake] 플러그인 빌드 구조 만들기 (--export-dynamic 활용)</title>
      <link>https://i5i5.tistory.com/1647</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CMake 환경에서 플러그인 빌드 구조를 만들기 위한 방법을 소개한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 설명한 플러그인 구조 및 요구사항은 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- main.cpp -&amp;gt; plugin.cpp의 .so파일을 load한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- plugin.cpp -&amp;gt; 공유라이브러리로 구현되어 산출물은 .so 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 플러그인이 main.cpp 측에 구현된 C++ 클래스를 사용이 가능하도록 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;1. 프로젝트 구조&lt;/h2&gt;
&lt;pre id=&quot;code_1778691229658&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.
├── CMakeLists.txt
├── Log.h          # main.cpp와 plugin.cpp가 공유하는 인터페이스
├── main.cpp       # 실행 파일 (구현체 포함)
└── plugin.cpp     # 공유 라이브러리&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 소스코드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Log.h&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778691297970&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once
#include &amp;lt;iostream&amp;gt;

class Log {
public:
    // 구현은 main.cpp에만 둘 예정
    void print();
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;main.cpp&lt;/b&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8&quot;&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;실행 파일 (main)이 Log::print의 실체를 가지고 있으며, 플러그인 (plugin.cpp)을 로드한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778691357664&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;dlfcn.h&amp;gt;
#include &quot;Log.h&quot;

// Log 클래스의 구현부 - 실행 파일 A에 위치함
void Log::print() {
    std::cout &amp;lt;&amp;lt; &quot;Hello World from A's Log class!&quot; &amp;lt;&amp;lt; std::endl;
}

int main() {
    // 플러그인 B(libpluginB.so)를 로드
    void* handle = dlopen(&quot;./libpluginB.so&quot;, RTLD_NOW);
    if (!handle) {
        std::cerr &amp;lt;&amp;lt; &quot;Cannot open library: &quot; &amp;lt;&amp;lt; dlerror() &amp;lt;&amp;lt; std::endl;
        return 1;
    }

    // 플러그인 내부의 'run_plugin' 함수 호출
    typedef void (*run_func)();
    run_func run = (run_func)dlsym(handle, &quot;run_plugin&quot;);
    
    if (run) {
        run();
    } else {
        std::cerr &amp;lt;&amp;lt; &quot;Cannot load symbol 'run_plugin': &quot; &amp;lt;&amp;lt; dlerror() &amp;lt;&amp;lt; std::endl;
    }

    dlclose(handle);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;plugin.cpp (공유 라이브러리)&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11&quot;&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;이 plugin은 Log.h만 알고 있고, 구현체가 없지만 Log 객체를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778691424437&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Log.h&quot;

extern &quot;C&quot; void run_plugin() {
    Log logger;
    // 플러그인에는 print()의 구현이 없지만, 실행 시 main쪽의 심볼을 찾아서 호출한다.
    logger.print();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;CMakeLists.txt&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 --export-dynamic 설정이 핵심이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778691479775&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.10)
project(ExportDynamicTest)

# 1. 실행 파일 A 생성
add_executable(AppA main.cpp)

# --export-dynamic 옵션 부여 (A의 심볼을 외부에 노출)
target_link_options(AppA PRIVATE &quot;-Wl,--export-dynamic&quot;)
# dlopen 기능을 위해 dl 라이브러리 링크 (리눅스)
target_link_libraries(AppA PRIVATE ${CMAKE_DL_LIBS})

# 2. 공유 라이브러리 B 생성
# 주의: 여기서는 Log.cpp(구현부)를 절대 추가하지 않습니다.
add_library(pluginB SHARED plugin.cpp)

# B는 링크 시점에 Log::print 심볼이 없어도 무시하도록 설정 (Undefined symbols)
# 하지만 보통 Linux 링커는 공유 라이브러리 빌드 시 
# 정의되지 않은 심볼이 있어도 기본적으로 허용합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 테스트 및 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;rm -rf build
mkdir build &amp;amp;&amp;amp; cd build
cmake ..
make&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./AppA
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;Hello World from A's Log class!
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 고찰&lt;/h2&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;만약 AppA의 target_link_options에서 -Wl,--export-dynamic을 &lt;b data-index-in-node=&quot;53&quot; data-path-to-node=&quot;22&quot;&gt;제거&lt;/b&gt;하고 다시 빌드하면 어떻게 될까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;23&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AppA는 문제없이 실행되지만, dlopen으로 pluginB를 로드하는 순간 다음과 같은 에러가 발생하며 중단된다.&lt;/li&gt;
&lt;li data-path-to-node=&quot;23,0,1&quot;&gt;undefined symbol: _ZN3Log5printEv (Log::print()의 망글링된 이름)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;24&quot; data-ke-size=&quot;size16&quot;&gt;이는 pluginB가 자신의 메모리 안에서 Log::print를 찾지 못했고, 메인 프로그램(AppA)이 그 심볼을 공개하지 않았기 때문에 발생하는 현상이다. 따라서 --export-dynamic은 &quot;내 안에 구현된 클래스 기능을 너(플러그인)에게 공유해줄게&quot;라는 선언과 같다.&lt;/p&gt;</description>
      <category>Build(빌드)/CMake</category>
      <category>export-dynamic</category>
      <category>Shared Library</category>
      <category>플러그인</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1647</guid>
      <comments>https://i5i5.tistory.com/1647#entry1647comment</comments>
      <pubDate>Thu, 14 May 2026 02:11:51 +0900</pubDate>
    </item>
    <item>
      <title>[CMake] target_include_directories와 target_link_libraries 개념</title>
      <link>https://i5i5.tistory.com/1645</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;target_include_directories vs target_link_libraries 키워드&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 명령어 모두 PUBLIC / PRIVATE / INTERFACE 키워드를 사용하지만, &lt;b&gt;전파되는 속성(property)이 다르기 때문에&lt;/b&gt; 의미와 효과가 다릅니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 개념: 두 가지 관점&lt;/h3&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;관점&lt;/td&gt;
&lt;td&gt;속성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;헤더(컴파일)&lt;/td&gt;
&lt;td&gt;INCLUDE_DIRECTORIES, INTERFACE_INCLUDE_DIRECTORIES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;링크&lt;/td&gt;
&lt;td&gt;LINK_LIBRARIES, INTERFACE_LINK_LIBRARIES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키워드는 &lt;b&gt;&quot;이 타겟 자신에게 적용되는가&quot;&lt;/b&gt; 와 &lt;b&gt;&quot;이 타겟에 의존하는 타겟에게도 전파되는가&quot;&lt;/b&gt; 를 동시에 제어합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;키워드 의미 (공통)&lt;/h3&gt;
&lt;pre id=&quot;code_1778431861480&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;               나 자신에게 적용?   의존자에게 전파?
PRIVATE         ✅                  ❌
INTERFACE       ❌                  ✅
PUBLIC          ✅                  ✅&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;target_include_directories&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컴파일 시&lt;/b&gt; 헤더 탐색 경로(-I)를 제어합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778431896139&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;target_include_directories(&amp;lt;target&amp;gt;
    PUBLIC    &amp;lt;include_dir&amp;gt;   # 나도 쓰고, 나를 쓰는 놈도 써야 함
    PRIVATE   &amp;lt;include_dir&amp;gt;   # 나만 씀 (구현 내부 헤더)
    INTERFACE &amp;lt;include_dir&amp;gt;   # 나는 안 씀, 나를 쓰는 놈만 써야 함
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실제 예시&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778431918176&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;project/
├── libfoo/
│   ├── include/foo/foo.h      &amp;larr; public API 헤더
│   ├── src/foo.cpp
│   └── src/internal/detail.h  &amp;larr; 내부 구현 헤더
└── app/
    └── main.cpp               &amp;larr; #include &amp;lt;foo/foo.h&amp;gt; 사용&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778431927674&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# libfoo/CMakeLists.txt
add_library(foo STATIC src/foo.cpp)

target_include_directories(foo
    PUBLIC  ${CMAKE_CURRENT_SOURCE_DIR}/include     # foo.h &amp;rarr; app도 필요
    PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/internal # detail.h &amp;rarr; foo만 필요
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778431936761&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# app/CMakeLists.txt
add_executable(app main.cpp)
target_link_libraries(app PRIVATE foo)
# &amp;rarr; app은 자동으로 include/foo 경로를 얻음 (foo의 PUBLIC이 전파됨)
# &amp;rarr; src/internal 경로는 전파 안 됨&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전파되는 CMake 내부 속성&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 72px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;키워드&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;자신의 속성&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;전파되는 속성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;PRIVATE&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;INCLUDE_DIRECTORIES&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;INTERFACE&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&amp;mdash;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;INTERFACE_INCLUDE_DIRECTORIES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;PUBLIC&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;INCLUDE_DIRECTORIES&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;INTERFACE_INCLUDE_DIRECTORIES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;target_link_libraries&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;링크 시&lt;/b&gt; 의존 라이브러리(-l, -L)를 제어합니다. 단, 키워드가 없으면 헤더 경로까지 함께 전파됩니다 (transitive dependency).&lt;/p&gt;
&lt;pre id=&quot;code_1778431982474&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;target_link_libraries(&amp;lt;target&amp;gt;
    PUBLIC    &amp;lt;lib&amp;gt;   # 나도 링크, 나를 쓰는 놈도 링크해야 함
    PRIVATE   &amp;lt;lib&amp;gt;   # 나만 링크 (구현 내부에서만 사용)
    INTERFACE &amp;lt;lib&amp;gt;   # 나는 링크 안 함, 나를 쓰는 놈이 링크해야 함
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 예시: 3계층 의존성&lt;/p&gt;
&lt;pre id=&quot;code_1778432002364&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;app &amp;rarr; libbar &amp;rarr; libfoo&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778432013340&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# libfoo
add_library(foo STATIC foo.cpp)
target_include_directories(foo PUBLIC include)

# libbar: foo를 PUBLIC으로 링크 &amp;rarr; bar의 헤더에서 foo 타입을 노출함
add_library(bar STATIC bar.cpp)
target_link_libraries(bar PUBLIC foo)
#   bar.h 안에 foo::SomeType 사용 &amp;rarr; app도 foo 헤더가 필요

# app
add_executable(app main.cpp)
target_link_libraries(app PRIVATE bar)
# &amp;rarr; app은 bar도 링크, foo도 자동으로 링크됨 (bar의 PUBLIC 전파)
# &amp;rarr; app은 foo의 include 경로도 자동으로 얻음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778432024450&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 반대 케이스: foo가 bar 내부 구현에서만 쓰임
target_link_libraries(bar PRIVATE foo)
# &amp;rarr; app은 foo를 링크 안 해도 됨
# &amp;rarr; app은 foo 헤더 경로도 받지 않음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전파되는 CMake 내부 속성&lt;/h4&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;키워드&lt;/td&gt;
&lt;td&gt;자신의 속성&lt;/td&gt;
&lt;td&gt;전파되는 속성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PRIVATE&lt;/td&gt;
&lt;td&gt;LINK_LIBRARIES&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INTERFACE&lt;/td&gt;
&lt;td&gt;&amp;mdash;&lt;/td&gt;
&lt;td&gt;INTERFACE_LINK_LIBRARIES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUBLIC&lt;/td&gt;
&lt;td&gt;LINK_LIBRARIES&lt;/td&gt;
&lt;td&gt;INTERFACE_LINK_LIBRARIES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 명령어의 관계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target_link_libraries로 CMake 타겟을 연결하면, 링크뿐 아니라 그 타겟의 &lt;b&gt;INTERFACE_INCLUDE_DIRECTORIES도 자동으로 전파&lt;/b&gt;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778432066907&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;target_link_libraries(app PRIVATE foo)
# 이 한 줄이 아래 두 줄을 암묵적으로 수행:
#   - app이 foo를 링크 (-lfoo)
#   - foo의 INTERFACE_INCLUDE_DIRECTORIES &amp;rarr; app의 INCLUDE_DIRECTORIES로 추가&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;올바르게 구성된 라이브러리&lt;/b&gt;라면 target_link_libraries 하나만으로 충분하고, 별도의 target_include_directories를 소비자 쪽에서 쓸 필요가 없습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;판단 기준 요약&lt;/h3&gt;
&lt;pre id=&quot;code_1778432091017&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;이 헤더/라이브러리가...
├── 내 .cpp 구현 파일에서만 쓰임?           &amp;rarr; PRIVATE
├── 내 공개 헤더(.h)에 노출됨?              &amp;rarr; PUBLIC
└── 내 헤더에만 노출, 내 .cpp엔 없음?       &amp;rarr; INTERFACE
    (header-only 라이브러리가 대표적)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Build(빌드)/CMake</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1645</guid>
      <comments>https://i5i5.tistory.com/1645#entry1645comment</comments>
      <pubDate>Mon, 11 May 2026 01:55:07 +0900</pubDate>
    </item>
    <item>
      <title>[CMake] CMake 명령어 순서에 대한 고찰</title>
      <link>https://i5i5.tistory.com/1644</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CMake는 imperative(명령형) 언어로 스크립트를 위에서 아래로 순차 실행하게 된다. 따라서, target이 존재하기 전에 그 target을 참조하면 오류가 나게 되는데, CMake 명령어 순서에 대해 정리해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;순서 규칙&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;1. add_library / add_executable &amp;rarr; 반드시 target_* 보다 먼저 선언한다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target을 생성해야 그 target에 속성을 붙일 수 있으므로 add_* 는 반드시 target_* 명령어보다 먼저 선언한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778424057808&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 잘못된 예: 타겟이 아직 없음
target_link_libraries(mylib PRIVATE somelib)
add_library(mylib src.cpp)

# 올바른 예
add_library(mylib src.cpp)
target_link_libraries(mylib PRIVATE somelib)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. target_* 명령어들 간의 순서 &amp;rarr; 대체로 무관하다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target_link_libraries, target_include_directories, target_compile_options 등은 타겟 생성 이후라면 &lt;b&gt;상호 간 순서는 결과에 영향 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778424100793&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;add_library(mylib src.cpp)

# 아래 셋의 순서는 바꿔도 동일한 결과
target_include_directories(mylib PUBLIC include/)
target_compile_options(mylib PRIVATE -Wall)
target_link_libraries(mylib PRIVATE pthread)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. find_package / pkg_check_modules &amp;rarr; 사용 전에 먼저&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어들은 import target이나 변수를 생성하므로, target_link_libraries에서 참조하기 전에 호출해야 합니다. import target에 대한 개념은 아래의 부록에서 설명한다.&lt;/p&gt;
&lt;pre id=&quot;code_1778424133977&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;find_package(sdbus-c++ REQUIRED)   # 먼저

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)  # 이후&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 서브디렉토리 간 의존성 &amp;rarr; add_subdirectory 순서가 중요&lt;/h3&gt;
&lt;pre id=&quot;code_1778424154472&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;add_subdirectory(libfoo)   # foo 타겟 생성
add_subdirectory(app)      # app이 foo에 의존 &amp;rarr; foo가 먼저여야 함&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, target_link_libraries로 연결된 타겟은 같은 CMake 빌드 트리 내라면 순서가 바뀌어도 CMake가 해결해 주는 경우도 있다. 하지만 명시적 순서를 유지하는 것이 안전하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 공식 문서&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;target_link_libraries 문서에 &lt;b&gt;&quot;target이 먼저 생성되어 있어야 한다&quot;&lt;/b&gt; 는 제약이 명시되어 있다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;문서&lt;/td&gt;
&lt;td&gt;내용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/add_library.html&quot;&gt;add_library&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;타겟 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/command/target_link_libraries.html&quot;&gt;target_link_libraries&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&quot;The named &amp;lt;target&amp;gt; must have been created by a command such as add_executable() or add_library()&quot; 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html&quot;&gt;cmake-buildsystem(7)&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;타겟/의존성 전체 개념 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href=&quot;https://cmake.org/cmake/help/latest/guide/tutorial/index.html&quot;&gt;CMake Tutorial&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;실제 순서를 보여주는 예제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;권장 관용적 순서&lt;/h2&gt;
&lt;pre id=&quot;code_1778424260815&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. 의존성 탐색
find_package(...)
pkg_check_modules(...)

# 2. 타겟 생성
add_library(mylib ...)
add_executable(myapp ...)

# 3. 타겟 속성 설정 (순서 무관)
target_include_directories(...)
target_compile_definitions(...)
target_compile_options(...)
target_link_libraries(...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서가 CMake 공식 튜토리얼과 대부분의 오픈소스 프로젝트에서 따르는 관례이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;부록&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 임포트 타겟 (Imported Target) 과 변수 (Variable)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 임포트 타겟 (Imported target)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;find_package가 성공하면, CMake가 &lt;b&gt;가상의 타겟&lt;/b&gt;을 생성하게 된다. 이 때, 실제 .a 혹은 .so 파일이 아니라, CMake 내부에만 존재하는 타겟이다.&lt;/p&gt;
&lt;pre id=&quot;code_1778426197996&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;find_package(sdbus-c++ REQUIRED)
# &amp;rarr; SDBusCpp::sdbus-c++ 라는 임포트 타겟이 생성됨

target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
#                                   ^^^^^^^^^^^^^^^^^^
#                                   이 타겟을 참조&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 임포트 타겟 안에는 아래 정보가 &lt;b&gt;모두 내장&lt;/b&gt;되어 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;링크할 .so 경로&lt;/li&gt;
&lt;li&gt;필요한 include 경로&lt;/li&gt;
&lt;li&gt;필요한 컴파일 옵션&lt;/li&gt;
&lt;li&gt;추이적 의존성 (transitive dependencies)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 target_link_libraries 한 줄만 써도 include/link가 전부 해결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■&amp;nbsp;변수 (Variable)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pkg_check_modules 또는 구버전 find_package는 임포트 타겟 대신 &lt;b&gt;변수&lt;/b&gt;를 설정한다:&lt;/p&gt;
&lt;pre id=&quot;code_1778426264707&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pkg_check_modules(DBUS REQUIRED dbus-1)
# &amp;rarr; 아래 변수들이 생성됨:
#   DBUS_INCLUDE_DIRS  = /usr/include/dbus-1.0 ...
#   DBUS_LIBRARIES     = dbus-1
#   DBUS_CFLAGS        = ...
#   DBUS_FOUND         = TRUE

target_include_directories(myapp PRIVATE ${DBUS_INCLUDE_DIRS})
target_link_libraries(myapp PRIVATE ${DBUS_LIBRARIES})
#                                   ^^^^^^^^^^^^^^^^
#                                   변수를 직접 참조&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수는 단순 문자열이므로, 직접 꺼내서 써야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 비교 요약&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;임포트 타겟&lt;/td&gt;
&lt;td&gt;변수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생성 방식&lt;/td&gt;
&lt;td&gt;find_package (modern)&lt;/td&gt;
&lt;td&gt;pkg_check_modules, 구버전 find_package&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사용 방법&lt;/td&gt;
&lt;td&gt;Foo::Bar 형태로 직접 전달&lt;/td&gt;
&lt;td&gt;${FOO_LIBRARIES} 등 변수로 분리 전달&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;include/link&lt;/td&gt;
&lt;td&gt;타겟 안에 통합&lt;/td&gt;
&lt;td&gt;변수를 각각 따로 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추이적 의존성&lt;/td&gt;
&lt;td&gt;자동 전파&lt;/td&gt;
&lt;td&gt;수동 관리 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 여부&lt;/td&gt;
&lt;td&gt;Modern CMake&lt;b&gt; 권장&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;레거시 방식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;■ 순서 측면&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1778429047307&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ❌ find_package 전에 참조
target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
find_package(sdbus-c++ REQUIRED)
# &amp;rarr; SDBusCpp::sdbus-c++ 타겟이 아직 존재하지 않으므로 오류&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1778429088297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ✅ find_package 후에 참조
find_package(sdbus-c++ REQUIRED)
# &amp;rarr; SDBusCpp::sdbus-c++ 타겟 생성 완료

target_link_libraries(myapp PRIVATE SDBusCpp::sdbus-c++)
# &amp;rarr; 정상 참조 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;add_library로 직접 만드는 타겟과 동일한 이유이다. 즉, &lt;b&gt;존재하기 전에 참조할 수 없다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size16&quot;&gt;추가 궁금증&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Q. install()은 명령어 순서는 중요하지 않습니까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. &lt;b&gt;타겟 생성(add_library/add_executable) 이후라면 어디서든 괜찮다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;install()은 빌드 시점이 아니라 &lt;b&gt;cmake --install 단계에서만 실행&lt;/b&gt;되는 명령어이기 때문에, 빌드 의존성과 무관하다.&lt;/p&gt;</description>
      <category>Build(빌드)/CMake</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1644</guid>
      <comments>https://i5i5.tistory.com/1644#entry1644comment</comments>
      <pubDate>Mon, 11 May 2026 01:12:31 +0900</pubDate>
    </item>
    <item>
      <title>(작성중) yocto 변수</title>
      <link>https://i5i5.tistory.com/1643</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://docs.yoctoproject.org/ref-manual/variables.html&quot;&gt;https://docs.yoctoproject.org/ref-manual/variables.html&lt;/a&gt;&lt;/p&gt;
&lt;figure data-og-url=&quot;&quot; data-og-image=&quot;&quot; data-og-source-url=&quot;https://docs.yoctoproject.org/ref-manual/variables.html&quot; data-og-host=&quot;&quot; data-og-description=&quot;&quot; data-ke-align=&quot;alignCenter&quot; data-og-title=&quot;&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a href=&quot;https://i5i5.tistory.com/manage/newpost/1642?type=post&amp;amp;returnURL=ENTRY&quot; data-source-url=&quot;https://docs.yoctoproject.org/ref-manual/variables.html&quot;&gt;
&lt;div style=&quot;background-image: url('\'\'');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이 홈페이지에서 원하는 변수명을 쉽게검색하려면 아래처럼 term- 뒤에 원하는 변수명을 검색해보자&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://docs.yoctoproject.org/ref-manual/variables.html#term-&quot;&gt;https://docs.yoctoproject.org/ref-manual/variables.html#term-&lt;/a&gt;&amp;lt;원하는 변수명&amp;gt;&lt;br /&gt;&lt;br /&gt;자주 쓰이는 변수&lt;br /&gt;&lt;br /&gt;${D}&lt;br /&gt;Destination directory. Build Directory 안에 있는 위치인데 그 위치에 component 들이 do_install task에 의해 설치된다.&lt;br /&gt;( -&amp;gt;&amp;nbsp;&amp;nbsp;${WORKDIR}/image )&lt;br /&gt;&lt;br /&gt;${S}&lt;br /&gt;Build Directory 안에 위치인데, 그 위치 안에 unpacked recipe source code 들이 있다.&amp;nbsp;&amp;nbsp;기본 값으로 이 directory는 ${UNPACKDIR}/${BPN}-${PV]이다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;${PN}&lt;br /&gt;레시피 이름 혹은 산출된 패키지 이름&lt;/p&gt;</description>
      <category>Build(빌드)/Yocto</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1643</guid>
      <comments>https://i5i5.tistory.com/1643#entry1643comment</comments>
      <pubDate>Sun, 10 May 2026 23:12:51 +0900</pubDate>
    </item>
    <item>
      <title>[DBUS] gdbus-codegen 명령어로 API Docs(Documentation) 생성하기</title>
      <link>https://i5i5.tistory.com/1601</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;gdbus-codegen 명령어로 documentation (XML 파일)을 생성하고 이를 HTML 파일로 나타내보자.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;STEP 1. 패키지 설치하기&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) libglib2.0-dev 패키지&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 gdbus-codegen 명령어를 실행하기 위해 libglib2.0-dev 패키지를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1769841671544&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 설치
sudo apt-get update
sudo apt-get install libglib2.0-dev

# 또는
sudo apt-get install libglib2.0-dev-bin&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이후 아래 명령어로 설치확인을 해보자&lt;/p&gt;
&lt;pre id=&quot;code_1769841684472&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# gdbus-codegen 존재 확인
which gdbus-codegen
# 예상 출력: /usr/bin/gdbus-codegen

# 버전 확인
# gdbus-codegen은 GLib의 일부이므로 버전이 동일
pkg-config --modversion glib-2.0
# 예상 출력: 2.72.4&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) gtk-doc-tools 패키지&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;xml을 html로 변환하기 위해 gtk-doc 명령어를 써야하는데, 그러기 위해 필요한 패키지를 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1769850043434&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install gtk-doc-tools&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 2. gdbus-codegen 명령어로 xml 형태의 Docs 생성&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 예제 DBus Instropection XML 파일을 생성하자&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;- org.example.Calculator.xml&lt;/p&gt;
&lt;pre id=&quot;code_1769844113481&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE node PUBLIC
&quot;-//freedesktop//DTD D-BUS Object Introspection 1.0//EN&quot;
&quot;http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd&quot;&amp;gt;
&amp;lt;node name=&quot;/&quot; xmlns:doc=&quot;http://www.freedesktop.org/dbus/1.0/doc.dtd&quot;&amp;gt;
  &amp;lt;interface name=&quot;org.example.Calculator&quot;&amp;gt;
      &amp;lt;!--
        Add:
        @a: The first double-precision floating-point number.
        @b: The second double-precision floating-point number.
        @result: The calculated sum of the two inputs.
        @response: server staus
        The @response indicates the server status:
        &amp;lt;simplelist&amp;gt;
              &amp;lt;member&amp;gt;0: Success, the request is carried out&amp;lt;/member&amp;gt;
              &amp;lt;member&amp;gt;1: Server cancelled the interaction&amp;lt;/member&amp;gt;
              &amp;lt;member&amp;gt;2: Sever interaction was ended in some other way&amp;lt;/member&amp;gt;
        &amp;lt;/simplelist&amp;gt;
        In the case of an error (response 2) @results may contain wrong value

        Computes the sum of @a and @b.
      --&amp;gt;
  	&amp;lt;method name=&quot;Add&quot;&amp;gt;
      &amp;lt;arg name=&quot;a&quot; type=&quot;d&quot; direction=&quot;in&quot;&amp;gt;
      &amp;lt;/arg&amp;gt;
      &amp;lt;arg name=&quot;b&quot; type=&quot;d&quot; direction=&quot;in&quot;&amp;gt;
      &amp;lt;/arg&amp;gt;
      &amp;lt;arg name=&quot;result&quot; type=&quot;d&quot; direction=&quot;out&quot;&amp;gt;
      &amp;lt;/arg&amp;gt;
      &amp;lt;arg name=&quot;response&quot; type=&quot;d&quot; direction=&quot;out&quot;&amp;gt;
      &amp;lt;/arg&amp;gt;
    &amp;lt;/method&amp;gt;
	&amp;lt;!--
        Subtract:
        @a: The minuend (value to be subtracted from).
        @b: The subtrahend (value to subtract).
        @result: The arithmetic difference.

        Subtracts @b from @a and returns the difference.
  	--&amp;gt;
    &amp;lt;method name=&quot;Subtract&quot;&amp;gt;
      &amp;lt;arg name=&quot;a&quot; type=&quot;d&quot; direction=&quot;in&quot; /&amp;gt;
      &amp;lt;arg name=&quot;b&quot; type=&quot;d&quot; direction=&quot;in&quot; /&amp;gt;
      &amp;lt;arg name=&quot;result&quot; type=&quot;d&quot; direction=&quot;out&quot; /&amp;gt;
    &amp;lt;/method&amp;gt;
    
	&amp;lt;!--
        CalculationFinished:
        @operation: A string representing the method name (e.g., &quot;Add&quot;).

        Emitted when a calculation is successfully completed.

        Example @version:
        &amp;lt;variablelist&amp;gt;
          &amp;lt;varlistentry&amp;gt;
            &amp;lt;term&amp;gt;version s&amp;lt;/term&amp;gt;
             &amp;lt;listitem&amp;gt;&amp;lt;para&amp;gt;
             Version is version.
            &amp;lt;/para&amp;gt;&amp;lt;/listitem&amp;gt;
          &amp;lt;/varlistentry&amp;gt;
          &amp;lt;varlistentry&amp;gt;
            &amp;lt;term&amp;gt;isStable b&amp;lt;/term&amp;gt;
            &amp;lt;listitem&amp;gt;&amp;lt;para&amp;gt;
            chekc if It's stable or not
            &amp;lt;/para&amp;gt;&amp;lt;/listitem&amp;gt;
          &amp;lt;/varlistentry&amp;gt;
	&amp;lt;/variablelist&amp;gt;
	Example description (@operation is good):

	Example description (@operation is good):

	Example description (@operation is good):
	--&amp;gt;
    &amp;lt;signal name=&quot;CalculationFinished&quot;&amp;gt;
      &amp;lt;arg name=&quot;operation&quot; type=&quot;s&quot;&amp;gt;
      &amp;lt;/arg&amp;gt;
    &amp;lt;/signal&amp;gt;
  &amp;lt;/interface&amp;gt;
&amp;lt;/node&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이후 해당 org.example.Calculator.xml파일이 위치한 곳에서,&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;gdbus-codegen 명령어를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1769848654461&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;gdbus-codegen --generate-docbook=docs \
org.example.Calculator.xml&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이렇게하면 docs-org.example.Calculator.xml 파일이 생성될 것이다.&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;STEP 3. Docs XML파일을 HTML로 변환하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docs-org.example.Calculator.xml 파일이 있는 위치에서 아래의 명령어를 실행한다&lt;/p&gt;
&lt;pre id=&quot;code_1769850177140&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mkdir -p html
cd html
gtkdoc-mkhtml docs ../docs-org.example.Calculator.xml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;STEP 4. index.html 파일 실행하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 index.html파일을 실행하면 아래와 같이 API Docs가 생성된 걸 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;1278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cljTrR/dJMcacBXHzX/wkUyqdjH9PGsBd9rJCrrO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cljTrR/dJMcacBXHzX/wkUyqdjH9PGsBd9rJCrrO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cljTrR/dJMcacBXHzX/wkUyqdjH9PGsBd9rJCrrO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcljTrR%2FdJMcacBXHzX%2FwkUyqdjH9PGsBd9rJCrrO1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1076&quot; height=&quot;1278&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;1278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Reference&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/flatpak/flatpak/blob/c27af8a9d90af5573b74f832a079a498caf5d1d1/data/org.freedesktop.Flatpak.Authenticator.xml#L26&quot;&gt;https://github.com/flatpak/flatpak/blob/c27af8a9d90af5573b74f832a079a498caf5d1d1/data/org.freedesktop.Flatpak.Authenticator.xml#L26&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DBUS/DBUS</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1601</guid>
      <comments>https://i5i5.tistory.com/1601#entry1601comment</comments>
      <pubDate>Thu, 30 Apr 2026 19:09:10 +0900</pubDate>
    </item>
    <item>
      <title>[C++] Singleton weak_ptr 사용</title>
      <link>https://i5i5.tistory.com/1641</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;memory&amp;gt;
#include &amp;lt;mutex&amp;gt;

// ============================================================
//&amp;nbsp;&amp;nbsp;Singleton: shared_ptr + weak_ptr 기반 수명 관리
//&amp;nbsp;&amp;nbsp;- 외부 shared_ptr이 모두 소멸 → ref count=0 → 자동 소멸
//&amp;nbsp;&amp;nbsp;- weak_ptr은 count에 영향 없음 (소유권 없는 관찰자)
// ============================================================
class Singleton {
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton(const Singleton&amp;amp;)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;= delete;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton&amp;amp; operator=(const Singleton&amp;amp;) = delete;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton(Singleton&amp;amp;&amp;amp;)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; = delete;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton&amp;amp; operator=(Singleton&amp;amp;&amp;amp;)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;= delete;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// private 소멸자에 접근 가능한 custom deleter
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;struct Deleter {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void operator()(Singleton* p) const { delete p; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static std::shared_ptr&amp;lt;Singleton&amp;gt; getInstance() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::lock_guard&amp;lt;std::mutex&amp;gt; lock(mutex_);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto sp = instance_.lock();&amp;nbsp;&amp;nbsp; // weak_ptr → shared_ptr 승격 시도
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!sp) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sp = std::shared_ptr&amp;lt;Singleton&amp;gt;(new Singleton(), Deleter{});
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;instance_ = sp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // weak_ptr 갱신 (count 영향 없음)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return sp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void doSomething() const {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;doSomething() called. value=&quot; &amp;lt;&amp;lt; value_ &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void setValue(int v) { value_ = v; }
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;&amp;nbsp;getValue() const { return value_; }

private:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Singleton() : value_(0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[+] Singleton constructed\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~Singleton() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[-] Singleton destroyed\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static std::weak_ptr&amp;lt;Singleton&amp;gt; instance_;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static std::mutex&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; mutex_;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int value_;
};

std::weak_ptr&amp;lt;Singleton&amp;gt; Singleton::instance_;
std::mutex&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Singleton::mutex_;


// ============================================================
//&amp;nbsp;&amp;nbsp;전달 방식별 ref count 변화 확인용 함수들
// ============================================================

// 1) 값 전달 → 복사 → count+1
void byValue(std::shared_ptr&amp;lt;Singleton&amp;gt; sp) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;[byValue]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;use_count=&quot; &amp;lt;&amp;lt; sp.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
}

// 2) const 참조 전달 → count 변화 없음
void byConstRef(const std::shared_ptr&amp;lt;Singleton&amp;gt;&amp;amp; sp) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;[byConstRef] use_count=&quot; &amp;lt;&amp;lt; sp.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
}

// 3) move 전달 → 소유권 이전, count 변화 없음, 원본 nullptr
void byMove(std::shared_ptr&amp;lt;Singleton&amp;gt; sp) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;[byMove]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; use_count=&quot; &amp;lt;&amp;lt; sp.use_count()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;(소유권 이전됨)\n&quot;;
}


int main() {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;=== 1. 기본 생성 및 소멸 ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s1 = Singleton::getInstance();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// count=1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;s1 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s2 = Singleton::getInstance();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// count=2
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;s2 use_count=&quot; &amp;lt;&amp;lt; s2.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp; // s2 소멸 → count=1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;s2 소멸 후 s1 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp; // s1 소멸 → count=0 → ~Singleton() 호출

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n=== 2. 소멸 후 재생성 ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s3 = Singleton::getInstance();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 새로 생성
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s3-&amp;gt;setValue(99);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s3-&amp;gt;doSomething();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp; // 소멸

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n=== 3. 값 전달 (count+1) ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s1 = Singleton::getInstance();&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// count=1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;호출 전 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;byValue(s1);&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 함수 내 count=2
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;호출 후 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;; // count=1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n=== 4. const 참조 전달 (count 유지) ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s1 = Singleton::getInstance();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;호출 전 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;byConstRef(s1);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;호출 후 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n=== 5. move 전달 (소유권 이전) ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s1 = Singleton::getInstance();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;move 전 use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;byMove(std::move(s1));&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // s1 → nullptr
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;move 후 s1 is nullptr? &quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; (s1 == nullptr ? &quot;YES&quot; : &quot;NO&quot;) &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;&amp;nbsp; // byMove 내부 sp 소멸 → count=0 → ~Singleton()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n=== 6. 동일 인스턴스 확인 ===\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s1 = Singleton::getInstance();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto s2 = Singleton::getInstance();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;same instance? &quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; (s1.get() == s2.get() ? &quot;YES&quot; : &quot;NO&quot;) &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;&amp;nbsp;&amp;nbsp;use_count=&quot; &amp;lt;&amp;lt; s1.use_count() &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;\n[main 종료]\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# C++17 이상 필요
g++ -std=c++17 -Wall -Wextra -o singleton main.cpp
./singleton&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;</description>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1641</guid>
      <comments>https://i5i5.tistory.com/1641#entry1641comment</comments>
      <pubDate>Wed, 29 Apr 2026 12:46:12 +0900</pubDate>
    </item>
    <item>
      <title>[sdbus] sdbus 예제 05 - timer 예제 (sd_event 방법)</title>
      <link>https://i5i5.tistory.com/1637</link>
      <description>&lt;h2 data-ke-size=&quot;size16&quot;&gt;개요&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sdbus에서 내부적으로 timer를 구현하려면 어떻게해야할까? 측, 특정 시간 이후 특정 콜백을 수행하게 하려면 어떻게 해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째는 std::thread + sleep를 활용한 방법이 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번쨰는 sd_event 타이머 활용(systemd 환경)하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 방법(std::thread + sleep)은 C++에서 전통적인 방법이다. 하지만 이 방법은 dbus 이벤트 루프와 통합되지 않고 별도로 콜백이 수행이 되기 때문에, 임계구역에 대해 신경을 써주어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 방법(sd_event 타이머 활용)은 sd_event를 활용하여 타이머를 구현하는 방법이다. 이 방법으로 하면, 타이머 이후 수행되는 콜백은 이벤트 루프 스레드에서 살행된다. sdbus-c++가 내부적으로 sd_event를 사용하므로 이 방법을 쓸 수 있다.&lt;/p&gt;
&lt;div&gt;
&lt;div data-is-streaming=&quot;false&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;두 방법을 비교하면 아래와 같다.&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;std::thread&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;sd_event 타이머&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;의존성&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;없음&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;libsystemd 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;정밀도&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;이벤트 루프 통합&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;별도 스레드&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;이벤트 루프와 통합&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;thread-safe&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;주의 필요&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;이벤트 루프 스레드에서 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 18.8372%;&quot;&gt;코드 복잡도&lt;/td&gt;
&lt;td style=&quot;width: 33.1395%;&quot;&gt;낮음&lt;/td&gt;
&lt;td style=&quot;width: 47.907%;&quot;&gt;높음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;systemd 환경이라면 &lt;b&gt;sd_event 타이머&lt;/b&gt;가 이벤트 루프와 통합되어 더 안전하고, 간단히 구현하려면 &lt;b&gt;std::thread&lt;/b&gt; 방식이 편리하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 sd_event 활용한 방법을 소개하겠다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;소스코드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트 구조&lt;/h3&gt;
&lt;pre id=&quot;code_1777216177071&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;project/
├── CMakeLists.txt
├── dbus/
│   └── calculator.xml
├── generated/
│   ├── calculator-adaptor.h
│   └── calculator-proxy.h
├── server.cpp
└── client.cpp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;server.cpp&lt;/h3&gt;
&lt;pre id=&quot;code_1777215779322&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;systemd/sd-event.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;csignal&amp;gt;
#include &amp;lt;cstring&amp;gt;
#include &quot;generated/calculator-adaptor.h&quot;

// 시간관련
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;iomanip&amp;gt;
#include &amp;lt;chrono&amp;gt;

// 시간 헬퍼 + 로그 매크로
static std::string currentTime()
{
    auto now    = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    auto ms     = std::chrono::duration_cast&amp;lt;std::chrono::milliseconds&amp;gt;(
                      now.time_since_epoch()) % 1000;

    std::ostringstream oss;
    oss &amp;lt;&amp;lt; std::put_time(std::localtime(&amp;amp;time_t), &quot;%Y-%m-%d %H:%M:%S&quot;)
        &amp;lt;&amp;lt; &quot;.&quot; &amp;lt;&amp;lt; std::setfill('0') &amp;lt;&amp;lt; std::setw(3) &amp;lt;&amp;lt; ms.count();

    return oss.str();
}

#define LOG(tag, msg) \
    std::cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; currentTime() &amp;lt;&amp;lt; &quot;] &quot; \
              &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; (tag) &amp;lt;&amp;lt; &quot;] &quot; &amp;lt;&amp;lt; msg &amp;lt;&amp;lt; &quot;\n&quot;

// 전역
static sd_event* g_event = nullptr;

static void onSignal(int)
{
    if (g_event)
        sd_event_exit(g_event, 0);
}

// CalculatorImpl
class CalculatorImpl
    : public sdbus::AdaptorInterfaces&amp;lt;org::example::Calculator_adaptor&amp;gt;
{
public:
    CalculatorImpl(sdbus::IConnection&amp;amp; conn, sdbus::ObjectPath path)
        : AdaptorInterfaces(conn, std::move(path))
    {
        registerAdaptor();
    }

    ~CalculatorImpl()
    {
        stopTimer();
        unregisterAdaptor();
    }

protected:
    int32_t Add(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
    {
        int32_t result = a + b;
        LOG(&quot;Add&quot;, a &amp;lt;&amp;lt; &quot; + &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; &quot; = &quot; &amp;lt;&amp;lt; result);

        // 이미 타이머 실행 중이면 재설정 안함
        if (timerSource_) {
            LOG(&quot;Timer&quot;, &quot;이미 실행 중 &amp;rarr; 재설정 안함&quot;);
        } else {
            startTimer();
        }

        return result;
    }

    int32_t Sub(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
    {
        int32_t result = a - b;
        LOG(&quot;Sub&quot;, a &amp;lt;&amp;lt; &quot; - &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; &quot; = &quot; &amp;lt;&amp;lt; result);

        // 타이머 해제
        stopTimer();

        return result;
    }

private:
    void startTimer()
    {
        sd_event* event = getObject().getConnection().getSdEventLoop();
        if (!event) {
            LOG(&quot;Timer&quot;, &quot;getSdEventLoop() = nullptr&quot;);
            return;
        }

        uint64_t now{};
        sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

        int r = sd_event_add_time(
            event,
            &amp;amp;timerSource_,
            CLOCK_MONOTONIC,
            now + 5 * 1'000'000,  // 5초 후
            0,
            onTimer,
            this
        );

        if (r &amp;lt; 0) {
            LOG(&quot;Timer&quot;, &quot;등록 실패: &quot; &amp;lt;&amp;lt; strerror(-r));
            return;
        }

        LOG(&quot;Timer&quot;, &quot;시작 (5초 후 onTimerFired 호출)&quot;);
    }

    void stopTimer()
    {
        if (!timerSource_) return;

        sd_event_source_unref(timerSource_);
        timerSource_ = nullptr;
        LOG(&quot;Timer&quot;, &quot;해제&quot;);
    }

    void onTimerFired()
    {
        LOG(&quot;Timer&quot;, &quot;Hello World!&quot;);
        stopTimer();
    }

    static int onTimer(sd_event_source* source,
                       uint64_t         usec,
                       void*            userdata)
    {
        auto* self = static_cast&amp;lt;CalculatorImpl*&amp;gt;(userdata);
        self-&amp;gt;onTimerFired();
        return 0;
    }

    sd_event_source* timerSource_{nullptr};
};

// main
int main()
{
    std::signal(SIGINT,  onSignal);
    std::signal(SIGTERM, onSignal);

    // 1. sd_event 생성
    int r = sd_event_default(&amp;amp;g_event);
    if (r &amp;lt; 0) {
        LOG(&quot;Server&quot;, &quot;sd_event_default 실패: &quot; &amp;lt;&amp;lt; strerror(-r));
        return 1;
    }

    // 2. D-Bus connection 생성
    auto conn = sdbus::createSessionBusConnection(
        sdbus::ServiceName{&quot;org.example.Calculator&quot;}
    );

    // 3. sdbus를 sd_event에 attach
    conn-&amp;gt;attachSdEventLoop(g_event);

    // 4. 서비스 등록
    CalculatorImpl calculator{
        *conn,
        sdbus::ObjectPath{&quot;/org/example/Calculator&quot;}
    };

    LOG(&quot;Server&quot;, &quot;Running... (Ctrl+C to stop)&quot;);
    LOG(&quot;Server&quot;, &quot;Add() &amp;rarr; 타이머 시작 (실행 중이면 재설정 안함)&quot;);
    LOG(&quot;Server&quot;, &quot;Sub() &amp;rarr; 타이머 해제&quot;);

    // 5. sd_event 루프 실행
    r = sd_event_loop(g_event);
    if (r &amp;lt; 0) {
        LOG(&quot;Server&quot;, &quot;sd_event_loop 실패: &quot; &amp;lt;&amp;lt; strerror(-r));
    }

    // 6. 정리
    conn-&amp;gt;detachSdEventLoop();
    sd_event_unref(g_event);
    g_event = nullptr;

    LOG(&quot;Server&quot;, &quot;Stopped&quot;);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;client.cpp&lt;/h3&gt;
&lt;pre id=&quot;code_1777215833505&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;sstream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &quot;generated/calculator-proxy.h&quot;

// 시간관련
#include &amp;lt;chrono&amp;gt;
#include &amp;lt;iomanip&amp;gt;

// 시간 헬퍼 + 로그 매크로
static std::string currentTime()
{
    auto now    = std::chrono::system_clock::now();
    auto time_t = std::chrono::system_clock::to_time_t(now);
    auto ms     = std::chrono::duration_cast&amp;lt;std::chrono::milliseconds&amp;gt;(
                      now.time_since_epoch()) % 1000;

    std::ostringstream oss;
    oss &amp;lt;&amp;lt; std::put_time(std::localtime(&amp;amp;time_t), &quot;%Y-%m-%d %H:%M:%S&quot;)
        &amp;lt;&amp;lt; &quot;.&quot; &amp;lt;&amp;lt; std::setfill('0') &amp;lt;&amp;lt; std::setw(3) &amp;lt;&amp;lt; ms.count();

    return oss.str();
}

#define LOG(tag, msg) \
    std::cout &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; currentTime() &amp;lt;&amp;lt; &quot;] &quot; \
              &amp;lt;&amp;lt; &quot;[&quot; &amp;lt;&amp;lt; (tag) &amp;lt;&amp;lt; &quot;] &quot; &amp;lt;&amp;lt; msg &amp;lt;&amp;lt; &quot;\n&quot;

// CalculatorClient
class CalculatorClient
    : public sdbus::ProxyInterfaces&amp;lt;org::example::Calculator_proxy&amp;gt;
{
public:
    CalculatorClient(sdbus::ServiceName dest, sdbus::ObjectPath path)
        : ProxyInterfaces(std::move(dest), std::move(path))
    {
        registerProxy();
    }

    ~CalculatorClient()
    {
        unregisterProxy();
    }
};

// main
int main()
{
    CalculatorClient client{
        sdbus::ServiceName{&quot;org.example.Calculator&quot;},
        sdbus::ObjectPath{&quot;/org/example/Calculator&quot;}
    };

    // 1. Add 호출 &amp;rarr; 타이머 시작
    LOG(&quot;Client&quot;, &quot;Add(10, 3) 호출&quot;);
    auto result1 = client.Add(10, 3);
    LOG(&quot;Client&quot;, &quot;결과: &quot; &amp;lt;&amp;lt; result1);

    sleep(1);

    // 2. Add 재호출 &amp;rarr; 이미 실행 중이므로 재설정 안함
    LOG(&quot;Client&quot;, &quot;Add(5, 2) 재호출 (타이머 실행 중)&quot;);
    auto result2 = client.Add(5, 2);
    LOG(&quot;Client&quot;, &quot;결과: &quot; &amp;lt;&amp;lt; result2);

    sleep(1);

    // 3. Sub 호출 &amp;rarr; 타이머 해제
    LOG(&quot;Client&quot;, &quot;Sub(10, 3) 호출 &amp;rarr; 타이머 해제&quot;);
    auto result3 = client.Sub(10, 3);
    LOG(&quot;Client&quot;, &quot;결과: &quot; &amp;lt;&amp;lt; result3);

    sleep(1);

    // 4. Add 재호출 &amp;rarr; 새 타이머 시작
    LOG(&quot;Client&quot;, &quot;Add(7, 1) 재호출 &amp;rarr; 새 타이머 시작&quot;);
    auto result4 = client.Add(7, 1);
    LOG(&quot;Client&quot;, &quot;결과: &quot; &amp;lt;&amp;lt; result4);

    // 5. 5초 대기 &amp;rarr; Hello World 출력 확인
    LOG(&quot;Client&quot;, &quot;5초 대기 중...&quot;);
    sleep(6);

    LOG(&quot;Client&quot;, &quot;종료&quot;);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size16&quot;&gt;CMakeLists.txt&lt;/h3&gt;
&lt;pre id=&quot;code_1777219148362&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.14)
project(timer_dbus_example CXX)
set(CMAKE_CXX_STANDARD 20)

find_package(sdbus-c++ REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SYSTEMD REQUIRED libsystemd)
find_program(SDBUS_XML2CPP sdbus-c++-xml2cpp REQUIRED)

add_custom_command(
    OUTPUT
        ${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
        ${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
    COMMAND ${SDBUS_XML2CPP}
        ${CMAKE_CURRENT_SOURCE_DIR}/dbus/calculator.xml
        --adaptor=${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
        --proxy=${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
    DEPENDS dbus/calculator.xml
    COMMENT &quot;Generating D-Bus glue code&quot;
)
add_custom_target(generate_dbus_glue DEPENDS
    ${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
    ${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
)

add_executable(server server.cpp)
target_include_directories(server PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/generated
    ${SYSTEMD_INCLUDE_DIRS}
)
target_link_libraries(server
    SDBusCpp::sdbus-c++
    ${SYSTEMD_LIBRARIES}
)
add_dependencies(server generate_dbus_glue)

add_executable(client client.cpp)
target_include_directories(client PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_CURRENT_SOURCE_DIR}/generated
)
target_link_libraries(client SDBusCpp::sdbus-c++)
add_dependencies(client generate_dbus_glue)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;빌드 및 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 빌드&lt;/p&gt;
&lt;pre id=&quot;code_1777216204773&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 빌드
rm -rf build generated
mkdir generated
mkdir build &amp;amp;&amp;amp; cd build
cmake ..
make -j$(nproc)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실행&lt;/p&gt;
&lt;pre id=&quot;code_1777216217395&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 터미널 1: 서버
#./server

# 터미널 2: 클라이언트
#./client&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출력화면&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;server 측&lt;/p&gt;
&lt;pre id=&quot;code_1777216556983&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./server 
[2026-04-26 23:46:49.489] [Server] Running... (Ctrl+C to stop)
[2026-04-26 23:46:49.489] [Server] Add() &amp;rarr; 타이머 시작 (실행 중이면 재설정 안함)
[2026-04-26 23:46:49.489] [Server] Sub() &amp;rarr; 타이머 해제

// 1회차
[2026-04-26 23:46:53.916] [Add] 10 + 3 = 13
[2026-04-26 23:46:53.916] [Timer] 시작 (5초 후 onTimerFired 호출)
[2026-04-26 23:46:54.918] [Add] 5 + 2 = 7
[2026-04-26 23:46:54.918] [Timer] 이미 실행 중 &amp;rarr; 재설정 안함
[2026-04-26 23:46:55.919] [Sub] 10 - 3 = 7
[2026-04-26 23:46:55.919] [Timer] 해제
[2026-04-26 23:46:56.920] [Add] 7 + 1 = 8
[2026-04-26 23:46:56.920] [Timer] 시작 (5초 후 onTimerFired 호출)
[2026-04-26 23:47:02.059] [Timer] Hello World!
[2026-04-26 23:47:02.059] [Timer] 해제

// 2회차
[2026-04-26 23:54:40.578] [Add] 10 + 3 = 13
[2026-04-26 23:54:40.578] [Timer] 시작 (5초 후 onTimerFired 호출)
[2026-04-26 23:54:41.578] [Add] 5 + 2 = 7
[2026-04-26 23:54:41.578] [Timer] 이미 실행 중 &amp;rarr; 재설정 안함
[2026-04-26 23:54:42.579] [Sub] 10 - 3 = 7
[2026-04-26 23:54:42.579] [Timer] 해제
[2026-04-26 23:54:43.580] [Add] 7 + 1 = 8
[2026-04-26 23:54:43.580] [Timer] 시작 (5초 후 onTimerFired 호출)
[2026-04-26 23:54:48.809] [Timer] Hello World!
[2026-04-26 23:54:48.809] [Timer] 해제&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;client 측&lt;/p&gt;
&lt;pre id=&quot;code_1777216616958&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1회차
$ ./client 
[2026-04-26 23:46:53.916] [Client] Add(10, 3) 호출
[2026-04-26 23:46:53.917] [Client] 결과: 13
[2026-04-26 23:46:54.917] [Client] Add(5, 2) 재호출 (타이머 실행 중)
[2026-04-26 23:46:54.918] [Client] 결과: 7
[2026-04-26 23:46:55.918] [Client] Sub(10, 3) 호출 &amp;rarr; 타이머 해제
[2026-04-26 23:46:55.919] [Client] 결과: 7
[2026-04-26 23:46:56.919] [Client] Add(7, 1) 재호출 &amp;rarr; 새 타이머 시작
[2026-04-26 23:46:56.920] [Client] 결과: 8
[2026-04-26 23:46:56.921] [Client] 5초 대기 중...
[2026-04-26 23:47:02.921] [Client] 종료

// 2회차
$ ./client 
[2026-04-26 23:54:40.577] [Client] Add(10, 3) 호출
[2026-04-26 23:54:40.578] [Client] 결과: 13
[2026-04-26 23:54:41.578] [Client] Add(5, 2) 재호출 (타이머 실행 중)
[2026-04-26 23:54:41.579] [Client] 결과: 7
[2026-04-26 23:54:42.579] [Client] Sub(10, 3) 호출 &amp;rarr; 타이머 해제
[2026-04-26 23:54:42.579] [Client] 결과: 7
[2026-04-26 23:54:43.580] [Client] Add(7, 1) 재호출 &amp;rarr; 새 타이머 시작
[2026-04-26 23:54:43.581] [Client] 결과: 8
[2026-04-26 23:54:43.581] [Client] 5초 대기 중...
[2026-04-26 23:54:49.581] [Client] 종료&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 client에서 Sub(타이머 해제하는 요청)을 보내지 않는다면, server측 출력은 아래와 같이 출력된다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1777217109803&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ ./server 
[2026-04-27 00:23:24.552] [Server] Running... (Ctrl+C to stop)
[2026-04-27 00:23:24.552] [Server] Add() &amp;rarr; 타이머 시작 (실행 중이면 재설정 안함)
[2026-04-27 00:23:24.553] [Server] Sub() &amp;rarr; 타이머 해제

[2026-04-27 00:23:28.275] [Add] 10 + 3 = 13
[2026-04-27 00:23:28.275] [Timer] 시작 (5초 후 onTimerFired 호출)
[2026-04-27 00:23:29.276] [Add] 5 + 2 = 7
[2026-04-27 00:23:29.277] [Timer] 이미 실행 중 &amp;rarr; 재설정 안함
[2026-04-27 00:23:30.278] [Add] 7 + 1 = 8
[2026-04-27 00:23:30.278] [Timer] 이미 실행 중 &amp;rarr; 재설정 안함
[2026-04-27 00:23:33.309] [Timer] Hello World!
[2026-04-27 00:23:33.309] [Timer] 해제&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DBUS/sdbus</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1637</guid>
      <comments>https://i5i5.tistory.com/1637#entry1637comment</comments>
      <pubDate>Fri, 24 Apr 2026 17:14:24 +0900</pubDate>
    </item>
    <item>
      <title>[sdbus] sdbus 예제 04 - Signal 구독 예제 (+ sdbus-c++-xml2cpp 활용)</title>
      <link>https://i5i5.tistory.com/1636</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Bus에서 전송(emit)하는 시그널을 구독하는 방법을 소개한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size23&quot;&gt;소스코드&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로젝트 구조&lt;/h3&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;project/
├── CMakeLists.txt
├── dbus/
│&amp;nbsp;&amp;nbsp; └── calculator.xml
├── generated/
│&amp;nbsp;&amp;nbsp; ├── calculator-adaptor.h
│&amp;nbsp;&amp;nbsp; └── calculator-proxy.h
├── server.cpp
└── client.cpp&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CMakeLists.txt&lt;/h3&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;cmake_minimum_required(VERSION 3.14)
project(signal_example CXX)
set(CMAKE_CXX_STANDARD 20)

find_package(sdbus-c++ REQUIRED)
find_program(SDBUS_XML2CPP sdbus-c++-xml2cpp REQUIRED)

add_custom_command(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;OUTPUT
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;COMMAND ${SDBUS_XML2CPP}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/dbus/calculator.xml
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--adaptor=${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;--proxy=${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DEPENDS dbus/calculator.xml
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;COMMENT &quot;Generating D-Bus glue code&quot;
)
add_custom_target(generate_dbus_glue DEPENDS
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
)

add_executable(server server.cpp)
target_include_directories(server PRIVATE
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated
)
target_link_libraries(server SDBusCpp::sdbus-c++)
add_dependencies(server generate_dbus_glue)

add_executable(client client.cpp)
target_include_directories(client PRIVATE
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${CMAKE_CURRENT_SOURCE_DIR}/generated
)
target_link_libraries(client SDBusCpp::sdbus-c++)
add_dependencies(client generate_dbus_glue)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;dbus/calculator.xml&lt;/h3&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;node&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;lt;interface name=&quot;org.example.Calculator&quot;&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;method name=&quot;Add&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;a&quot; type=&quot;i&quot; direction=&quot;in&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;b&quot; type=&quot;i&quot; direction=&quot;in&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;result&quot; type=&quot;i&quot; direction=&quot;out&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/method&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;method name=&quot;Sub&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;a&quot; type=&quot;i&quot; direction=&quot;in&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;b&quot; type=&quot;i&quot; direction=&quot;in&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;result&quot; type=&quot;i&quot; direction=&quot;out&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/method&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;signal name=&quot;ResultCalculated&quot;&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;operation&quot; type=&quot;s&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;arg name=&quot;result&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;type=&quot;i&quot;/&amp;gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/signal&amp;gt;

&amp;nbsp;&amp;nbsp;&amp;lt;/interface&amp;gt;
&amp;lt;/node&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;server.cpp&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;csignal&amp;gt;
#include &quot;generated/calculator-adaptor.h&quot;

static sdbus::IConnection* g_conn = nullptr;

static void onSignal(int)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (g_conn)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g_conn-&amp;gt;leaveEventLoop();
}

class CalculatorImpl
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: public sdbus::AdaptorInterfaces&amp;lt;org::example::Calculator_adaptor&amp;gt;
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CalculatorImpl(sdbus::IConnection&amp;amp; conn, sdbus::ObjectPath path)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: AdaptorInterfaces(conn, std::move(path))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerAdaptor();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~CalculatorImpl()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unregisterAdaptor();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

protected:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t Add(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t result = a + b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Add(&quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; b
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot;) = &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// signal emit
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emitResultCalculated(&quot;Add&quot;, result);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return result;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t Sub(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t result = a - b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Sub(&quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; b
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot;) = &quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// signal emit
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emitResultCalculated(&quot;Sub&quot;, result);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return result;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
};

int main()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::signal(SIGINT,&amp;nbsp;&amp;nbsp;onSignal);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::signal(SIGTERM, onSignal);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto conn = sdbus::createSessionBusConnection(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ServiceName{&quot;org.example.Calculator&quot;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g_conn = conn.get();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CalculatorImpl calculator{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*conn,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ObjectPath{&quot;/org/example/Calculator&quot;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Running... (Ctrl+C to stop)\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;enterEventLoop();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Stopped\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;client.cpp (proxy 상속 없이 uponSignal 사용)&lt;/h3&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ proxy 직접 생성 (상속 없음)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto proxy = sdbus::createProxy(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ServiceName{&quot;org.example.Calculator&quot;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ObjectPath{&quot;/org/example/Calculator&quot;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::InterfaceName ifaceName{&quot;org.example.Calculator&quot;};

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ uponSignal로 signal 구독
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proxy-&amp;gt;uponSignal(sdbus::SignalName{&quot;ResultCalculated&quot;})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .onInterface(ifaceName)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .call([](const std::string&amp;amp; operation, const int32_t&amp;amp; result) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; std::cout &amp;lt;&amp;lt; &quot;[Signal] ResultCalculated:&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&amp;lt; &quot; operation=&quot; &amp;lt;&amp;lt; operation
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;&amp;lt; &quot; result=&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; });

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Add 호출
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Add(10, 3) 호출\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t addResult{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proxy-&amp;gt;callMethod(&quot;Add&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .onInterface(ifaceName)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .withArguments(int32_t{10}, int32_t{3})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .storeResultsTo(addResult);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Add 결과: &quot; &amp;lt;&amp;lt; addResult &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sleep(1);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Sub 호출
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Sub(10, 3) 호출\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t subResult{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;proxy-&amp;gt;callMethod(&quot;Sub&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .onInterface(ifaceName)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .withArguments(int32_t{10}, int32_t{3})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; .storeResultsTo(subResult);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Sub 결과: &quot; &amp;lt;&amp;lt; subResult &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sleep(1);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;만약 bus servie 이름과 object 이름을 생략하고 싶다면 addMatch를 이용한다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre class=&quot;c++ arduino&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

int main()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto conn = sdbus::createSessionBusConnection();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ interface명과 signal명만으로 구독
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const std::string matchRule =
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;type='signal',&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;interface='org.example.Calculator',&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;member='ResultCalculated'&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;addMatch(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;matchRule,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[](sdbus::Message msg) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::string operation{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;msg &amp;gt;&amp;gt; operation &amp;gt;&amp;gt; result;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Signal] operation=&quot; &amp;lt;&amp;lt; operation
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot; result=&quot; &amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] signal 대기중...\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;enterEventLoop();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;만약 proxy를 상속하여 사용하고 싶은 경우, 아래의 client.cpp 코드를 참고한다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre class=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
#include &quot;generated/calculator-proxy.h&quot;

class CalculatorClient
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: public sdbus::ProxyInterfaces&amp;lt;org::example::Calculator_proxy&amp;gt;
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CalculatorClient(sdbus::ServiceName dest, sdbus::ObjectPath path)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: ProxyInterfaces(std::move(dest), std::move(path))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerProxy();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~CalculatorClient()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unregisterProxy();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

protected:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// signal 수신 콜백
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void onResultCalculated(const std::string&amp;amp; operation,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;const int32_t&amp;amp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; result) override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Signal] ResultCalculated:&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot; operation=&quot; &amp;lt;&amp;lt; operation
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; &quot; result=&quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;&amp;lt; result &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
};

int main()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CalculatorClient client{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ServiceName{&quot;org.example.Calculator&quot;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ObjectPath{&quot;/org/example/Calculator&quot;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Add 호출 &amp;rarr; 서버에서 ResultCalculated signal emit
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Add(10, 3) 호출\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto addResult = client.Add(10, 3);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Add 결과: &quot; &amp;lt;&amp;lt; addResult &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sleep(1);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Sub 호출 &amp;rarr; 서버에서 ResultCalculated signal emit
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Sub(10, 3) 호출\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto subResult = client.Sub(10, 3);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Client] Sub 결과: &quot; &amp;lt;&amp;lt; subResult &amp;lt;&amp;lt; &quot;\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sleep(1);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빌드 및 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 빌드&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;rm -rf build
rm -rf generated
mkdir generated
mkdir build &amp;amp;&amp;amp; cd build
cmake ..
make -j$(nproc)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;- 실행&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# 터미널 1: 서버
#./server

# 터미널 2: 클라이언트
#./client&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;출력결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- server측 터미널&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;[Server] Running... (Ctrl+C to stop)
[Server] Add(10, 3) = 13
[Server] Sub(10, 3) = 7

[Server] Add(10, 3) = 13
[Server] Sub(10, 3) = 7
[Server] Stopped&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;- client측 터미널&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ ./client 
[Client] Add(10, 3) 호출
[Client] Add 결과: 13
[Signal] ResultCalculated: operation=Add result=13
[Client] Sub(10, 3) 호출
[Client] Sub 결과: 7
[Signal] ResultCalculated: operation=Sub result=7

$ ./client 
[Client] Add(10, 3) 호출
[Signal] ResultCalculated: operation=Add result=13
[Client] Add 결과: 13
[Client] Sub(10, 3) 호출
[Client] Sub 결과: 7
[Signal] ResultCalculated: operation=Sub result=7&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DBUS/sdbus</category>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1636</guid>
      <comments>https://i5i5.tistory.com/1636#entry1636comment</comments>
      <pubDate>Fri, 24 Apr 2026 01:37:36 +0900</pubDate>
    </item>
    <item>
      <title>(작성중( sdbus timer</title>
      <link>https://i5i5.tistory.com/1634</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;cmake_minimum_required(VERSION 3.14)&lt;br&gt;project(timer_example CXX)&lt;br&gt;set(CMAKE_CXX_STANDARD 20)&lt;br&gt;&lt;br&gt;find_package(sdbus-c++ REQUIRED)&lt;br&gt;&lt;br&gt;find_package(PkgConfig REQUIRED)&lt;br&gt;pkg_check_modules(SYSTEMD REQUIRED libsystemd)&lt;br&gt;&lt;br&gt;add_executable(server server.cpp)&lt;br&gt;target_include_directories(server PRIVATE ${SYSTEMD_INCLUDE_DIRS})&lt;br&gt;target_link_libraries(server&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SDBusCpp::sdbus-c++&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;${SYSTEMD_LIBRARIES}&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;&lt;br&gt;#include &amp;lt;systemd/sd-event.h&amp;gt;&lt;br&gt;#include &amp;lt;iostream&amp;gt;&lt;br&gt;#include &amp;lt;csignal&amp;gt;&lt;br&gt;&lt;br&gt;//------------------------------------------------------------&lt;br&gt;// 전역 sd_event (signal 핸들러에서 루프 종료용)&lt;br&gt;//------------------------------------------------------------&lt;br&gt;static sd_event* g_event = nullptr;&lt;br&gt;&lt;br&gt;static void onSignal(int)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (g_event)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_exit(g_event, 0);&lt;br&gt;}&lt;br&gt;&lt;br&gt;//------------------------------------------------------------&lt;br&gt;// TimerService: connection을 주입받아 타이머 관리&lt;br&gt;//------------------------------------------------------------&lt;br&gt;class TimerService&lt;br&gt;{&lt;br&gt;public:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;explicit TimerService(sdbus::IConnection&amp;amp; conn)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: mConn_(conn)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~TimerService()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void startTimer()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 기존 타이머 제거&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ getSdEventLoop()로 sd_event 핸들 가져오기&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = mConn_.getSdEventLoop();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!event) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;[Timer] sd_event loop not available\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int r = sd_event_add_time(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;timerSource_,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLOCK_MONOTONIC,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now + 5 * 1'000'000,&amp;nbsp;&amp;nbsp;// 5초 후 (microsecond)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onTimer,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r &amp;lt; 0) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;[Timer] 등록 실패: &quot; &amp;lt;&amp;lt; strerror(-r) &amp;lt;&amp;lt; &quot;\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 시작 (5초 후 onTimerFired 호출)\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void stopTimer()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!timerSource_) return;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_unref(timerSource_);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerSource_ = nullptr;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 해제\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;private:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void onTimerFired()&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 5초 경과 → onTimerFired 실행!\n&quot;;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// timerSource_ 정리 (단발성)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 원하는 작업 수행&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 작업 완료\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static int onTimer(sd_event_source* source,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint64_t&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; usec,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userdata)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* self = static_cast&amp;lt;TimerService*&amp;gt;(userdata);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self-&amp;gt;onTimerFired();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::IConnection&amp;amp; mConn_;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerSource_{nullptr};&lt;br&gt;};&lt;br&gt;&lt;br&gt;//------------------------------------------------------------&lt;br&gt;// main&lt;br&gt;//------------------------------------------------------------&lt;br&gt;int main()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 1. sd_event 생성&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event{};&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int r = sd_event_default(&amp;amp;event);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r &amp;lt; 0) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;sd_event_default failed: &quot; &amp;lt;&amp;lt; strerror(-r) &amp;lt;&amp;lt; &quot;\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g_event = event;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 2. SIGINT/SIGTERM 처리&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::signal(SIGINT,&amp;nbsp;&amp;nbsp;onSignal);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::signal(SIGTERM, onSignal);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 3. D-Bus connection 생성&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto conn = sdbus::createSessionBusConnection(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ServiceName{&quot;org.example.Calculator&quot;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 4. ✅ sdbus connection을 sd_event에 attach&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;attachSdEventLoop(event);&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 5. TimerService 생성 및 타이머 시작&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attachSdEventLoop() 이후에 getSdEventLoop()가 유효한 포인터 반환&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TimerService timerService{*conn};&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerService.startTimer();&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Running... (Ctrl+C to stop)\n&quot;;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 6. ✅ sd_event 루프 실행 (sdbus + 타이머 통합)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;r = sd_event_loop(event);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r &amp;lt; 0) {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;sd_event_loop failed: &quot; &amp;lt;&amp;lt; strerror(-r) &amp;lt;&amp;lt; &quot;\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 7. 정리&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;detachSdEventLoop();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_unref(event);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g_event = nullptr;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Server] Stopped\n&quot;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br&gt;}&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;# 빌드&lt;br&gt;mkdir build &amp;amp;&amp;amp; cd build&lt;br&gt;cmake ..&lt;br&gt;make -j$(nproc)&lt;br&gt;&lt;br&gt;# 실행&lt;br&gt;./server&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;#include &amp;lt;sdbus-c++/sdbus-c++.h&amp;gt;
#include &amp;lt;systemd/sd-event.h&amp;gt;
#include &amp;lt;iostream&amp;gt;

class TimerService
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 생성자에서 connection 주입
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;explicit TimerService(sdbus::IConnection&amp;amp; conn)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: mConnection_(conn)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~TimerService()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void startTimer()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 주입받은 connection으로 getEventLoopHandle() 호출
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = static_cast&amp;lt;sd_event*&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mConnection_.getEventLoopHandle()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int r = sd_event_add_time(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;timerSource_,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLOCK_MONOTONIC,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now + 5 * 1'000'000,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onTimer,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r &amp;lt; 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cerr &amp;lt;&amp;lt; &quot;[Timer] 등록 실패: &quot; &amp;lt;&amp;lt; strerror(-r) &amp;lt;&amp;lt; &quot;\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 시작 (5초 후 실행)\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void stopTimer()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!timerSource_) return;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_unref(timerSource_);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerSource_ = nullptr;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 해제\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

private:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void onTimerFired()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] 5초 경과 → onTimerFired 실행!\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 원하는 작업 수행
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static int onTimer(sd_event_source* source,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint64_t&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; usec,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userdata)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* self = static_cast&amp;lt;TimerService*&amp;gt;(userdata);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self-&amp;gt;onTimerFired();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::IConnection&amp;amp; mConnection_;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // ✅ 주입받은 connection
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerSource_{nullptr};
};&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;int main()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto conn = sdbus::createSessionBusConnection(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sdbus::ServiceName{&quot;org.example.Calculator&quot;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 같은 connection 주입
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TimerService timerService{*conn};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerService.startTimer();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn-&amp;gt;enterEventLoop();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;타이머해제방법&lt;br&gt;// ✅ 방법 1: SD_EVENT_OFF로 비활성화 (source는 유지)&lt;br&gt;sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);&lt;br&gt;&lt;br&gt;// ✅ 방법 2: unref로 완전 제거&lt;br&gt;sd_event_source_unref(timerSource_);&lt;br&gt;timerSource_ = nullptr;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;class CalculatorImpl
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: public sdbus::AdaptorInterfaces&amp;lt;org::example::Calculator_adaptor&amp;gt;
{
public:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CalculatorImpl(sdbus::IConnection&amp;amp; conn, sdbus::ObjectPath path)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;: AdaptorInterfaces(conn, std::move(path))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerAdaptor();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;setupTimer();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;~CalculatorImpl()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unregisterAdaptor();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

protected:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t Add(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Add 호출 시 타이머 시작
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;startTimer();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return a + b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int32_t Sub(const int32_t&amp;amp; a, const int32_t&amp;amp; b) override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Sub 호출 시 타이머 해제
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stopTimer();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return a - b;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

private:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void setupTimer()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = static_cast&amp;lt;sd_event*&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getObject().getConnection().getEventLoopHandle()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_add_time(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;timerSource_,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLOCK_MONOTONIC,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now + 5 * 1'000'000,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;onTimer,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 처음엔 비활성화 상태로 등록
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Registered (inactive)\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 타이머 시작
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void startTimer()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!timerSource_) return;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = static_cast&amp;lt;sd_event*&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getObject().getConnection().getEventLoopHandle()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 현재 시간 기준으로 재설정
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_time(timerSource_, now + 5 * 1'000'000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(timerSource_, SD_EVENT_ONESHOT);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Started\n&quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 타이머 해제
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;void stopTimer()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (!timerSource_) return;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 방법 1: 비활성화만 (재사용 가능)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(timerSource_, SD_EVENT_OFF);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Stopped\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 방법 2: 완전 제거 (재사용 불가)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// sd_event_source_unref(timerSource_);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// timerSource_ = nullptr;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static int onTimer(sd_event_source* source,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; uint64_t&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; usec,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; void*&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;userdata)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* self = static_cast&amp;lt;CalculatorImpl*&amp;gt;(userdata);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Fired!\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 반복 타이머
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_time(source, usec + 5 * 1'000'000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source* timerSource_{nullptr};
};&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;SD_EVENT_OFF vs unref 비교&lt;br&gt;항목&lt;br&gt;SD_EVENT_OFF&lt;br&gt;unref&lt;br&gt;source 메모리&lt;br&gt;유지&lt;br&gt;해제&lt;br&gt;재시작 가능&lt;br&gt;✅ set_enabled(ONESHOT)&lt;br&gt;❌ 재등록 필요&lt;br&gt;적합한 상황&lt;br&gt;일시 중지 후 재사용&lt;br&gt;완전히 제거&lt;br&gt;일시적으로 멈췄다가 다시 쓸 거라면 SD_EVENT_OFF, 완전히 제거할 거라면 unref 를 사용하면 됩니다.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;void setupTimer()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = static_cast&amp;lt;sd_event*&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getObject().getConnection().getEventLoopHandle()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ 람다를 heap에 저장 후 userdata로 전달
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* callback = new std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[this](sd_event_source* source, uint64_t usec) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Fired!\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 원하는 작업
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emitSubResult(42);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 반복 타이머
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_time(source, usec + 5 * 1'000'000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_add_time(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;timerSource_,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLOCK_MONOTONIC,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now + 5 * 1'000'000,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ wrapper: userdata를 std::function으로 캐스팅해서 호출
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[](sd_event_source* source, uint64_t usec, void* userdata) -&amp;gt; int {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* cb = static_cast&amp;lt;std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;*&amp;gt;(userdata);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(*cb)(source, usec);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;callback&amp;nbsp;&amp;nbsp;// userdata
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ source 소멸 시 람다 자동 정리
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_userdata(timerSource_, callback);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_destroy_callback(timerSource_,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[](void* userdata) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* cb = static_cast&amp;lt;std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;*&amp;gt;(userdata);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delete cb;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;br&gt;더깔끔하게ㅜ핼퍼로&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;// TimerHelper.h
#pragma once
#include &amp;lt;systemd/sd-event.h&amp;gt;
#include &amp;lt;functional&amp;gt;
#include &amp;lt;stdexcept&amp;gt;

inline sd_event_source* addTimer(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event*&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t&amp;nbsp;&amp;nbsp; intervalUs,&amp;nbsp;&amp;nbsp;// microsecond
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt; callback)
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;uint64_t now{};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_now(event, CLOCK_MONOTONIC, &amp;amp;now);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* cb = new std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::move(callback)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source* source{nullptr};
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int r = sd_event_add_time(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;amp;source,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CLOCK_MONOTONIC,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now + intervalUs,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[](sd_event_source* s, uint64_t usec, void* userdata) -&amp;gt; int {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;auto* cb = static_cast&amp;lt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;*&amp;gt;(userdata);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(*cb)(s, usec);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cb
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (r &amp;lt; 0) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delete cb;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw std::runtime_error(&quot;Failed to add timer&quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_destroy_callback(source,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[](void* userdata) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;delete static_cast&amp;lt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::function&amp;lt;void(sd_event_source*, uint64_t)&amp;gt;*&amp;gt;(userdata);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return source;
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;헬퍼함수 사용예시&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;c++&quot; data-ke-language=&quot;c++&quot;&gt;&lt;code&gt;#include &quot;TimerHelper.h&quot;

void setupTimer()
{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event* event = static_cast&amp;lt;sd_event*&amp;gt;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getObject().getConnection().getEventLoopHandle()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ✅ static 함수, this 전달 없이 깔끔하게 사용
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timerSource_ = addTimer(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;event,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;5 * 1'000'000,&amp;nbsp;&amp;nbsp;// 5초
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[this](sd_event_source* source, uint64_t usec) {
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;std::cout &amp;lt;&amp;lt; &quot;[Timer] Fired!\n&quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;emitSubResult(42);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 반복
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_time(source, usec + 5 * 1'000'000);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;);
}&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;</description>
      <author>i5</author>
      <guid isPermaLink="true">https://i5i5.tistory.com/1634</guid>
      <comments>https://i5i5.tistory.com/1634#entry1634comment</comments>
      <pubDate>Thu, 23 Apr 2026 15:55:55 +0900</pubDate>
    </item>
  </channel>
</rss>