반응형

plugin/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_)

반응형