sdbus
2026. 4. 13. 16:27plugin/ICalculatorPlugin.h
#pragma once
#include <sdbus-c++/sdbus-c++.h>
#include <dlfcn.h>
#include <memory>
#include <stdexcept>
#include <iostream>
class ICalculatorPlugin
{
public:
virtual ~ICalculatorPlugin()
{
if (lib_) {
std::cout << "[ICalculatorPlugin] dlclose\n";
dlclose(lib_);
}
}
virtual void start(sdbus::IConnection& conn) = 0;
void setLibHandle(void* lib) { lib_ = lib; }
private:
void* lib_{nullptr};
};
extern "C" {
ICalculatorPlugin* create_plugin();
}
inline std::unique_ptr<ICalculatorPlugin> load_plugin(const char* path)
{
void* lib = dlopen(path, RTLD_LAZY);
if (!lib) {
throw std::runtime_error(std::string("dlopen failed: ") + dlerror());
}
dlerror();
auto create = reinterpret_cast<ICalculatorPlugin*(*)()>(
dlsym(lib, "create_plugin"));
const char* err = dlerror();
if (err) {
dlclose(lib);
throw std::runtime_error(std::string("dlsym failed: ") + err);
}
auto plugin = std::unique_ptr<ICalculatorPlugin>(create());
plugin->setLibHandle(lib);
return plugin;
}
plugin/calculator_plugin.cpp
#include "ICalculatorPlugin.h"
#include "../generated/calculator-adaptor.h"
#include <iostream>
class CalculatorImpl
: public sdbus::AdaptorInterfaces<org::example::Calculator_adaptor>
{
public:
CalculatorImpl(sdbus::IConnection& conn, sdbus::ObjectPath path)
: AdaptorInterfaces(conn, std::move(path))
{
registerAdaptor();
}
~CalculatorImpl()
{
std::cout << "[CalculatorImpl] destroyed\n";
unregisterAdaptor();
}
protected:
int32_t Add(const int32_t& a, const int32_t& b) override
{
int32_t result = a + b;
std::cout << "[Plugin] Add(" << a << ", " << b << ") = " << result << "\n";
return result;
}
int32_t Sub(const int32_t& a, const int32_t& b) override
{
int32_t result = a - b;
std::cout << "[Plugin] Sub(" << a << ", " << b << ") = " << result << "\n";
emitSubResult(result);
return result;
}
};
class CalculatorPlugin : public ICalculatorPlugin
{
public:
~CalculatorPlugin()
{
std::cout << "[CalculatorPlugin] destroyed\n";
// impl_ unique_ptr 소멸 → ~CalculatorImpl() 자동 호출
}
void start(sdbus::IConnection& conn) override
{
impl_ = std::make_unique<CalculatorImpl>(
conn,
sdbus::ObjectPath{"/org/example/Calculator"}
);
std::cout << "[CalculatorPlugin] started\n";
}
private:
std::unique_ptr<CalculatorImpl> impl_;
};
extern "C" {
ICalculatorPlugin* create_plugin()
{
return new CalculatorPlugin();
}
}
server.cpp
#include <sdbus-c++/sdbus-c++.h>
#include "plugin/ICalculatorPlugin.h"
#include <iostream>
int main(int argc, char* argv[])
{
const char* libPath = (argc > 1) ? argv[1] : "./libcalculator_plugin.so";
// 플러그인 로딩
std::unique_ptr<ICalculatorPlugin> plugin;
try {
plugin = load_plugin(libPath);
} catch (const std::exception& e) {
std::cerr << e.what() << "\n";
return 1;
}
std::cout << "[Server] Loaded: " << libPath << "\n";
// D-Bus 연결
auto conn = sdbus::createSessionBusConnection(
sdbus::ServiceName{"org.example.Calculator"}
);
// 플러그인 시작
plugin->start(*conn);
std::cout << "[Server] Running...\n";
conn->enterEventLoop();
return 0;
// unique_ptr<ICalculatorPlugin> 소멸 순서:
// 1. ~CalculatorPlugin()
// 2. ~CalculatorImpl() → unregisterAdaptor()
// 3. ~ICalculatorPlugin() → dlclose()
}
client.cpp
#include <sdbus-c++/sdbus-c++.h>
#include "generated/calculator-proxy.h"
#include <iostream>
#include <unistd.h>
class CalculatorClient
: public sdbus::ProxyInterfaces<org::example::Calculator_proxy>
{
public:
CalculatorClient(sdbus::ServiceName dest, sdbus::ObjectPath path)
: ProxyInterfaces(std::move(dest), std::move(path))
{
registerProxy();
}
~CalculatorClient()
{
unregisterProxy();
}
protected:
void onSubResult(const int32_t& result) override
{
std::cout << "[Signal] SubResult received: " << result << "\n";
}
};
int main()
{
CalculatorClient client{
sdbus::ServiceName{"org.example.Calculator"},
sdbus::ObjectPath{"/org/example/Calculator"}
};
auto addResult = client.Add(10, 3);
std::cout << "10 + 3 = " << addResult << "\n";
auto subResult = client.Sub(10, 3);
std::cout << "10 - 3 = " << subResult << "\n";
sleep(1); // SubResult signal 수신 대기
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(calculator_example CXX)
set(CMAKE_CXX_STANDARD 20)
find_package(sdbus-c++ REQUIRED)
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 "Generating D-Bus glue code"
)
add_custom_target(generate_dbus_glue DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-adaptor.h
${CMAKE_CURRENT_SOURCE_DIR}/generated/calculator-proxy.h
)
# 동적 라이브러리 (플러그인)
add_library(calculator_plugin SHARED plugin/calculator_plugin.cpp)
target_include_directories(calculator_plugin PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/generated
)
target_link_libraries(calculator_plugin SDBusCpp::sdbus-c++)
add_dependencies(calculator_plugin generate_dbus_glue)
# 서버
add_executable(server server.cpp)
target_include_directories(server PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(server SDBusCpp::sdbus-c++ dl)
add_dependencies(server generate_dbus_glue)
# 클라이언트
add_executable(client client.cpp)
target_include_directories(client PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(client SDBusCpp::sdbus-c++)
add_dependencies(client generate_dbus_glue)
전체 소멸 순서 정리
main() 종료
│
└─ plugin (unique_ptr<ICalculatorPlugin>) 소멸
│
├─ 1. ~CalculatorPlugin()
│ └─ impl_.reset()
│ └─ ~CalculatorImpl()
│ └─ unregisterAdaptor()
│
└─ 2. ~ICalculatorPlugin()
└─ dlclose(lib_)