Plugin Development Quick Example
This document quickly introduces how to develop PoSDK plugins through simple examples. For detailed interface descriptions, please refer to Data Plugin Interface Details and Method Plugin Interface Details.
Plugin Registration System
All PoSDK plugins need to be registered through the REGISTRATION_PLUGIN macro, which is defined in pomvg_plugin_register.hpp (already included in po_core.hpp).
Registration Macro Syntax
REGISTRATION_PLUGIN(PluginClassName)
Parameter Description:
Parameter: Plugin class name (e.g.,
MyPlugin::MyMethod,MyPlugin::MyDataPlugin)Plugin type string is automatically read from CMake
PLUGIN_NAMEmacro
Important
** Automatic Implementation of GetType() Function**
Macro automatically implements
GetType()functionOnly declare
const std::string& GetType() const override;in header fileDo not manually implement
GetType()in source filePlugin name is defined once in CMakeLists.txt, automatically used by the macro
Notes:
Must be placed at the end of
.cppsource file (cannot be placed in.hppheader file)Plugin name must be defined in CMakeLists.txt using
add_posdk_plugin(plugin_name ...)Plugin type string is automatically read from CMake, ensuring consistency between CMake target, file name, and registration type
Method Plugin Development Quick Example
Method plugins are used to implement specific algorithm logic. PoSDK provides three Method base classes to choose from:
Base Class Selection Guide
Base Class |
Applicable Scenarios |
Required Override Functions |
Main Features |
|---|---|---|---|
|
Simple algorithms, no config needed |
|
Most basic interface |
|
Algorithms requiring config files |
|
Parameter management, input checking |
|
Algorithms requiring performance analysis |
|
Performance statistics, auto evaluation |
Recommendation: For research and comparison purposes, recommend using MethodPresetProfiler.
Example 1: Simple Method Plugin
Most basic Method plugin, suitable for quick prototype validation.
Code Example
CMakeLists.txt:
# Plugin name defined here (single definition point)
add_posdk_plugin(my_simple_method
PLUGIN_TYPE methods
SOURCES my_simple_method.cpp
HEADERS my_simple_method.hpp
)
my_simple_method.hpp:
#include <po_core.hpp>
namespace MyPlugin {
using namespace PoSDK;
using namespace Interface;
using namespace types;
class MySimpleMethod : public Method {
public:
// Only declare, automatically implemented by REGISTRATION_PLUGIN macro
const std::string& GetType() const override;
DataPtr Build(const DataPtr& material_ptr = nullptr) override;
};
} // namespace MyPlugin
my_simple_method.cpp:
#include "my_simple_method.hpp"
namespace MyPlugin {
// Don't need to manually implement GetType(), macro automatically generates
DataPtr MySimpleMethod::Build(const DataPtr& material_ptr) {
LOG_INFO_ZH << "执行简单算法";
LOG_INFO_EN << "Running simple algorithm";
// Implement algorithm logic...
// Return result
return std::make_shared<DataMap<std::string>>("算法执行完成", "data_map_string");
}
} // namespace MyPlugin
// ✅ Plugin registration - automatically reads plugin name from CMake PLUGIN_NAME
// Plugin type: "my_simple_method" (from CMake)
REGISTRATION_PLUGIN(MyPlugin::MySimpleMethod)
Usage
auto method = FactoryMethod::Create("my_simple_method");
auto result = method->Build();
Example 2: Method Plugin with Configuration
Using MethodPreset base class, supports configuration file management and input data checking.
Code Example
CMakeLists.txt:
# Plugin name defined here (single definition point)
add_posdk_plugin(my_preset_method
PLUGIN_TYPE methods
SOURCES my_preset_method.cpp
HEADERS my_preset_method.hpp
)
my_preset_method.hpp:
#include <po_core.hpp>
namespace MyPlugin {
using namespace PoSDK;
using namespace Interface;
using namespace types;
class MyPresetMethod : public MethodPreset {
public:
MyPresetMethod();
// Only declare, automatically implemented by REGISTRATION_PLUGIN macro
const std::string& GetType() const override;
DataPtr Run() override;
};
} // namespace MyPlugin
my_preset_method.cpp:
#include "my_preset_method.hpp"
namespace MyPlugin {
MyPresetMethod::MyPresetMethod() {
// Define required input data types
required_package_["data_tracks"] = nullptr;
required_package_["data_camera_models"] = nullptr;
// Load configuration file (configs/methods/my_preset_method.ini)
// method_options_ automatically loads from configuration file
InitializeDefaultConfigPath();
// Initialize log directory
InitializeLogDir();
}
// Don't need to manually implement GetType(), macro automatically generates
DataPtr MyPresetMethod::Run() {
// 1. Get input data (MethodPreset already automatically checks types)
// Recommended: use GetRequiredDataPtr for simplified access
auto tracks = GetRequiredDataPtr<Tracks>("data_tracks");
auto cameras = GetRequiredDataPtr<CameraModels>("data_camera_models");
if (!tracks || !cameras) {
LOG_ERROR_ZH << "缺少必要的输入数据";
LOG_ERROR_EN << "Missing required input data";
return nullptr;
}
// 2. Get configuration parameters (from config file or use defaults)
float threshold = GetOptionAsFloat("threshold", 0.5f);
int max_iter = GetOptionAsInt("max_iterations", 100);
LOG_INFO_ZH << "阈值=" << threshold << ", 最大迭代=" << max_iter;
LOG_INFO_EN << "Threshold=" << threshold << ", Max iterations=" << max_iter;
// 3. Implement core algorithm
// ... Algorithm implementation ...
// 4. Return result
return std::make_shared<DataMap<std::string>>("处理完成", "data_map_string");
}
} // namespace MyPlugin
// ✅ Plugin registration - automatically reads plugin name from CMake PLUGIN_NAME
// Plugin type: "my_preset_method" (from CMake)
REGISTRATION_PLUGIN(MyPlugin::MyPresetMethod)
Configuration File Example
configs/methods/my_preset_method.ini:
[my_preset_method]
threshold=0.8
max_iterations=200
output_path={root_dir}/results
Usage
// Create Method object
auto method = FactoryMethod::Create("my_preset_method");
// Prepare input data package
auto data_package = FactoryData::Create("data_package");
auto tracks_data = FactoryData::Create("data_tracks");
auto cameras_data = FactoryData::Create("data_camera_models");
// ... Populate data ...
data_package->Call("SetData", "data_tracks", &tracks_data);
data_package->Call("SetData", "data_camera_models", &cameras_data);
// Execute algorithm
auto result = method->Build(data_package);
Example 3: Method Plugin with Performance Analysis
Using MethodPresetProfiler base class, automatically performs performance statistics and evaluation.
Code Example
CMakeLists.txt:
# Plugin name defined here (single definition point)
add_posdk_plugin(my_profiler_method
PLUGIN_TYPE methods
SOURCES my_profiler_method.cpp
HEADERS my_profiler_method.hpp
)
my_profiler_method.hpp:
#include <po_core.hpp>
namespace MyPlugin {
using namespace PoSDK;
using namespace Interface;
using namespace types;
class MyProfilerMethod : public MethodPresetProfiler {
public:
MyProfilerMethod();
// Only declare, automatically implemented by REGISTRATION_PLUGIN macro
const std::string& GetType() const override;
DataPtr Run() override;
};
} // namespace MyPlugin
my_profiler_method.cpp:
#include "my_profiler_method.hpp"
namespace MyPlugin {
MyProfilerMethod::MyProfilerMethod() {
// Define required input data types
required_package_["data_tracks"] = nullptr;
// Load configuration file (configs/methods/my_profiler_method.ini)
// method_options_ automatically loads from configuration file
InitializeDefaultConfigPath();
InitializeLogDir();
}
// Don't need to manually implement GetType(), macro automatically generates
DataPtr MyProfilerMethod::Run() {
// ✅ Performance analysis: Start session (automatically uses method labels)
POSDK_START(true); // Default: TIME only to save overhead
// Performance analysis: Step 1 - Add stage checkpoint
PROFILER_STAGE("preprocessing");
// Preprocessing code...
// Performance analysis: Step 2 - Add stage checkpoint
PROFILER_STAGE("core_algorithm");
// Core algorithm code...
// Performance analysis: Step 3 - Add stage checkpoint
PROFILER_STAGE("postprocessing");
// Post-processing code...
// ✅ Performance analysis: End session (automatically submits data)
PROFILER_END();
return std::make_shared<DataMap<std::string>>("Processing finished", "data_map_string");
}
} // namespace MyPlugin
// ✅ Plugin registration - automatically reads plugin name from CMake PLUGIN_NAME
// Plugin type: "my_profiler_method" (from CMake)
REGISTRATION_PLUGIN(MyPlugin::MyProfilerMethod)
Performance Profiling Macro Notes:
POSDK_START(true): Start profiling session, automatically uses method’s configured labels, defaults to TIME only (saves overhead)PROFILER_STAGE("stage_name"): Add stage checkpoint, automatically calculates interval performance metricsPROFILER_END(): End session and automatically submit data to ProfilerManagerTo collect more metrics, use
POSDK_START(true, "time|memory|cpu")
Configuration File Example
configs/methods/my_profiler_method.ini:
[my_profiler_method]
threshold=0.7
enable_profiling=true
enable_evaluation=true
Usage
auto method = FactoryMethod::Create("my_profiler_method");
auto data_package = FactoryData::Create("data_package");
// ... Prepare data ...
auto result = method->Build(data_package);
// Performance statistics automatically exported to CSV file
DataIO Plugin Development Quick Example
DataIO plugins are used to encapsulate various data types. The simplest way is to inherit from DataIO base class.
Code Example
CMakeLists.txt:
# Plugin name defined here (single definition point)
add_posdk_plugin(my_custom_data
PLUGIN_TYPE data
SOURCES my_data_plugin.cpp
HEADERS my_data_plugin.hpp
)
my_data_plugin.hpp:
#include <po_core.hpp>
namespace MyPlugin {
using namespace PoSDK;
using namespace Interface;
struct CustomData {
int id;
std::string name;
std::vector<double> values;
};
class MyDataPlugin : public DataIO {
private:
CustomData data_;
public:
// Only declare, automatically implemented by REGISTRATION_PLUGIN macro
const std::string& GetType() const override;
void* GetData() override;
// Optional: implement Save/Load
bool Save(const std::string& folder = "",
const std::string& filename = "",
const std::string& extension = ".dat") override;
bool Load(const std::string& filepath = "",
const std::string& file_type = "dat") override;
};
} // namespace MyPlugin
my_data_plugin.cpp:
#include "my_data_plugin.hpp"
namespace MyPlugin {
// Don't need to manually implement GetType(), macro automatically generates
void* MyDataPlugin::GetData() {
return static_cast<void*>(&data_);
}
bool MyDataPlugin::Save(const std::string& folder,
const std::string& filename,
const std::string& extension) {
// Implement save logic...
return true;
}
bool MyDataPlugin::Load(const std::string& filepath,
const std::string& file_type) {
// Implement load logic...
return true;
}
} // namespace MyPlugin
// ✅ Plugin registration - automatically reads plugin name from CMake PLUGIN_NAME
// Plugin type: "my_custom_data" (from CMake)
REGISTRATION_PLUGIN(MyPlugin::MyDataPlugin)
Usage
// Create data object
auto data = FactoryData::Create("my_custom_data");
// Get internal data pointer
auto custom_data = GetDataPtr<CustomData>(data);
if (custom_data) {
custom_data->id = 1;
custom_data->name = "test";
custom_data->values = {1.0, 2.0, 3.0};
}
// Save data
data->Save("/path/to/folder", "my_data");
// Load data
auto loaded_data = FactoryData::Create("my_custom_data");
loaded_data->Load("/path/to/folder/my_data.dat");
Example 4: DataIO Plugin Supporting Protobuf Serialization
Using PbDataIO base class can easily implement protobuf serialization and persistence of data. This is PoSDK’s recommended data plugin implementation method, suitable for scenarios requiring cross-platform storage and version compatibility.
See also
For complete PbDataIO development guide, serialization macro usage instructions, and advanced features, please refer to Serialization Storage and Reading.
Code Example
CMakeLists.txt:
# Plugin name defined here (single definition point)
add_posdk_plugin(data_example
PLUGIN_TYPE data
SOURCES data_example.cpp
HEADERS data_example.hpp
)
data_example.hpp:
#include <po_core.hpp>
#include <proto/my_example.pb.h> // Protobuf generated header file
namespace MyPlugin {
using namespace PoSDK;
using namespace Interface;
using namespace types;
// Internal data structure
struct ExampleDataStruct {
int id = 0;
std::string name;
std::vector<double> values;
};
class DataExample : public PbDataIO {
private:
std::shared_ptr<ExampleDataStruct> data_ptr_;
public:
DataExample();
// Only declare, automatically implemented by REGISTRATION_PLUGIN macro
const std::string& GetType() const override;
void* GetData() override;
DataPtr CopyData() const override;
protected:
// Three protobuf interfaces that PbDataIO must implement
std::unique_ptr<google::protobuf::Message> CreateProtoMessage() const override;
std::unique_ptr<google::protobuf::Message> ToProto() const override;
bool FromProto(const google::protobuf::Message& message) override;
};
} // namespace MyPlugin
data_example.cpp:
#include "data_example.hpp"
namespace MyPlugin {
DataExample::DataExample() {
data_ptr_ = std::make_shared<ExampleDataStruct>();
// Set default storage directory
auto storage_folder = std::filesystem::current_path() / "storage" / "examples";
std::filesystem::create_directories(storage_folder);
SetStorageFolder(storage_folder);
}
// Don't need to manually implement GetType(), macro automatically generates
void* DataExample::GetData() {
return static_cast<void*>(data_ptr_.get());
}
DataPtr DataExample::CopyData() const {
auto cloned = std::make_shared<DataExample>();
auto cloned_struct = static_cast<ExampleDataStruct*>(cloned->GetData());
if (cloned_struct && data_ptr_) {
*cloned_struct = *data_ptr_;
}
cloned->SetStorageFolder(GetStorageFolder());
return cloned;
}
// ========== Protobuf Serialization Implementation ==========
std::unique_ptr<google::protobuf::Message> DataExample::CreateProtoMessage() const {
return std::make_unique<proto::ExampleData>();
}
std::unique_ptr<google::protobuf::Message> DataExample::ToProto() const {
auto proto_msg = std::make_unique<proto::ExampleData>();
if (!data_ptr_) return proto_msg;
// Use macros to simplify serialization
PROTO_SET_BASIC(proto_msg, id, data_ptr_->id);
PROTO_SET_BASIC(proto_msg, name, data_ptr_->name);
PROTO_SET_ARRAY(proto_msg, values, data_ptr_->values);
return proto_msg;
}
bool DataExample::FromProto(const google::protobuf::Message& message) {
const auto* proto_msg = dynamic_cast<const proto::ExampleData*>(&message);
if (!proto_msg) return false;
if (!data_ptr_) {
data_ptr_ = std::make_shared<ExampleDataStruct>();
}
// Use macros to simplify deserialization
PROTO_GET_BASIC(*proto_msg, id, data_ptr_->id);
PROTO_GET_BASIC(*proto_msg, name, data_ptr_->name);
PROTO_GET_ARRAY(*proto_msg, values, data_ptr_->values);
return true;
}
} // namespace MyPlugin
// ✅ Plugin registration - automatically reads plugin name from CMake PLUGIN_NAME
// Plugin type: "data_example" (from CMake)
REGISTRATION_PLUGIN(MyPlugin::DataExample)
Usage
// Create data object
auto data = FactoryData::Create("data_example");
// Populate data
auto example_data = GetDataPtr<ExampleDataStruct>(data);
if (example_data) {
example_data->id = 123;
example_data->name = "test_example";
example_data->values = {1.0, 2.0, 3.0, 4.0, 5.0};
}
// Save data (automatically serialized to protobuf format)
data->Save("/path/to/folder", "my_example");
// Load data (automatically deserialized)
auto loaded_data = FactoryData::Create("data_example");
loaded_data->Load("/path/to/folder/my_example.pb");
PbDataIO Core Advantages
Automatic File I/O:
Save()andLoad()automatically handle file read/write and serializationCross-platform Compatibility: Protobuf ensures data portability across different platforms
Convenient Macro Support: Provides
PROTO_SET_*andPROTO_GET_*macros to simplify serialization codePath Memory: After saving, can directly call
Load()without specifying path
Tip
When to Use PbDataIO?
Need to save data to disk and share between different sessions
Need cross-platform data exchange (Windows/Linux/macOS)
Data structure is complex, containing Eigen types, arrays, etc.
Need version compatibility (protobuf supports backward compatibility)
For detailed protobuf serialization macro list, .proto file definitions, advanced features, etc., please refer to Serialization Storage and Reading.
Key Points Summary
Method Plugin Development
Must Override:
**
GetType()**: Only declare in header file, automatically implemented byREGISTRATION_PLUGINmacroBuild()orRun(): Implement algorithm logic
Base Class Selection:
Simple algorithm →
MethodNeed configuration →
MethodPreset(recommended)Need performance analysis →
MethodPresetProfiler(recommended for research)
Configuration Management:
Call
InitializeDefaultConfigPath()in constructor to load configuration fileConfiguration file path:
configs/methods/<method_type>.iniUse
GetOptionAsXXX(key, default_value)to get parameters, returns default value when parameter doesn’t existmethod_options_automatically loads from.inifile, no manual setup needed
DataIO Plugin Development
Must Override:
**
GetType()**: Only declare in header file, automatically implemented byREGISTRATION_PLUGINmacroGetData(): Return internal data pointer
Optional Override:
Save()/Load(): Data persistenceCopyData(): Deep copy
Data Access:
Use
GetDataPtr<T>(data_ptr)to get typed pointer
Recommend Using PbDataIO (supports Protobuf serialization):
Inherit from
PbDataIObase classImplement three protobuf interfaces:
CreateProtoMessage(),ToProto(),FromProto()Use serialization macros to simplify code (
PROTO_SET_*,PROTO_GET_*)Automatically handles file I/O and cross-platform compatibility
See Serialization Storage and Reading for details
Plugin Registration
CMakeLists.txt:
add_posdk_plugin(plugin_name # ← Plugin name defined here
PLUGIN_TYPE methods # or "data"
SOURCES plugin.cpp
HEADERS plugin.hpp
)
C++ code:
// Must be at end of .cpp file - automatically implements GetType()
// Plugin name automatically read from CMake PLUGIN_NAME macro
REGISTRATION_PLUGIN(MyPlugin::PluginClassName)
Next Steps
Deep Learning: Data Plugin Interface Details
Deep Learning: Method Plugin Interface Details
View Available Types: Core Data Types
Performance Analysis Tools: Performance Analysis System
Evaluation System: Evaluation System