Skip to content

Latest commit

 

History

History
136 lines (101 loc) · 3.66 KB

MODULES_101.md

File metadata and controls

136 lines (101 loc) · 3.66 KB

Modules 101

The goal of this guide is to build a module as a dynamic library and load it at runtime

Documentation

Setup Build

Here is the repo architecture of this example:

.
├── CMakeLists.txt
└── src
    ├── main.cpp
    └── module.cpp

Let's create a CMakeLists to build our binary zia and our dynamic library module:

cmake_minimum_required(VERSION 3.17)

# The name of the CMake project
project(TestZia)

# The C++ standard you want to use for your project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_LIST_DIR})
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR})

add_executable(zia src/main.cpp)

# Fetch ZiAPI

include(ExternalProject)

ExternalProject_Add(
    ziapi
    GIT_REPOSITORY  https://github.com/martin-olivier/ZiAPI.git
    GIT_TAG         v5.0.0
    INSTALL_COMMAND ""
    TEST_COMMAND    ""
)

add_dependencies(zia ziapi)
ExternalProject_Get_Property(ziapi SOURCE_DIR)
include_directories(${SOURCE_DIR}/include)

if(UNIX)
    target_link_libraries(zia PRIVATE dl)
endif()

# Build Our Dynamic Lib

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})

add_library(module SHARED src/module.cpp)

# running the below CMake rule will allow you to remove the prefix `lib` for macOS and linux, ensuring
# that the library shares the same name on all the different OS:
# https://github.com/martin-olivier/dylib#remove-the-lib-prefix
set_target_properties(module PROPERTIES PREFIX "")

add_dependencies(module ziapi)

After that lets implement module.cpp that will be build into a dynamic lib:

#include "dylib/dylib.hpp"
#include "ziapi/Module.hpp"

class Module : public ziapi::IModule {
public:
    Module() = default;

    ~Module() override = default;

    void Init(const ziapi::config::Node &) override {}

    ziapi::Version GetVersion() const noexcept override { return {4, 0, 0}; }
    
    ziapi::Version GetCompatibleApiVersion() const noexcept override { return {4, 0, 0}; }

    [[nodiscard]] virtual const char *GetName() const noexcept override { return "module_name"; }

    [[nodiscard]] virtual const char *GetDescription() const noexcept override
    {
        return "A module implementation example";
    }
};

DYLIB_API ziapi::IModule *LoadZiaModule() { return new Module; }

⚠️ The function that returns a new module from a dynamic library MUST have this prototype:

DYLIB_API ziapi::IModule *LoadZiaModule()

And then let's implement our main.cpp that will load the dynamic lib:

#include "ziapi/Logger.hpp"
#include "ziapi/Module.hpp"
#include "dylib/dylib.hpp"

int main()
{
    try {
        // Create a dynamic lib object that will load the module
        dylib lib("./module", dylib::extension);
        // Get the function that will generate our module when called
        auto entry_point_fn = lib.get_function<ziapi::IModule *()>("LoadZiaModule");
        // Call the function to get a module instance
        std::unique_ptr<ziapi::IModule> mod(entry_point_fn());
        // Print information about the module using the logger
        ziapi::Logger::Info("Module loaded: ", mod->GetName(), " - ", mod->GetDescription());
    }
    catch (const dylib::exception &e) {
        // Catch exceptions around a dynamic lib (handle or symbol errors) and print them using the logger
        ziapi::Logger::Error(e.what());
    }
    return 0;
}

Let's run our binary:

> ./zia
Sun Jan 23 16:07:41 2022 [i] Module loaded: module_name - A module implementation example