Develop WasmEdge Plug-in in C++ API
We recommend developers to develop plug-ins in WasmEdge C API.
Prerequisites
For developing the WasmEdge plug-in in internal C++, developers should build WasmEdge from source.
Example
Assume that the plug-in example is in the file testplugin.h
and testplugin.cpp
.
Host Functions and Modules
The goal of the plug-in is to provide the host functions which can be imported when instantiating WASM.
Therefore, developers should implement their plug-in host functions in WasmEdge internal C++ first.
Assume that the host function implementations are in the testplugin.h
.
#pragma once
#include "plugin/plugin.h"
#include <cstdint>
#include <string>
namespace WasmEdge {
namespace Host {
// The environment class. For the register object.
class WasmEdgePluginTestEnv {
public:
WasmEdgePluginTestEnv() noexcept = default;
static Plugin::PluginRegister Register;
};
// The host function base template class. For inheriting the environment class
// reference.
template <typename T>
class WasmEdgePluginTestFunc : public Runtime::HostFunction<T> {
public:
WasmEdgePluginTestFunc(WasmEdgePluginTestEnv &HostEnv)
: Runtime::HostFunction<T>(0), Env(HostEnv) {}
protected:
WasmEdgePluginTestEnv &Env;
};
// The host function to add 2 int32_t numbers.
class WasmEdgePluginTestFuncAdd
: public WasmEdgePluginTestFunc<WasmEdgePluginTestFuncAdd> {
public:
WasmEdgePluginTestFuncAdd(WasmEdgePluginTestEnv &HostEnv)
: WasmEdgePluginTestFunc(HostEnv) {}
Expect<uint32_t> body(const Runtime::CallingFrame &, uint32_t A, uint32_t B) {
return A + B;
}
};
// The host function to sub 2 int32_t numbers.
class WasmEdgePluginTestFuncSub
: public WasmEdgePluginTestFunc<WasmEdgePluginTestFuncSub> {
public:
WasmEdgePluginTestFuncSub(WasmEdgePluginTestEnv &HostEnv)
: WasmEdgePluginTestFunc(HostEnv) {}
Expect<uint32_t> body(const Runtime::CallingFrame &, uint32_t A, uint32_t B) {
return A - B;
}
};
// The host module class. There can be several modules in a plug-in.
class WasmEdgePluginTestModule : public Runtime::Instance::ModuleInstance {
public:
WasmEdgePluginTestModule()
: Runtime::Instance::ModuleInstance("wasmedge_plugintest_cpp_module") {
addHostFunc("add", std::make_unique<WasmEdgePluginTestFuncAdd>(Env));
addHostFunc("sub", std::make_unique<WasmEdgePluginTestFuncSub>(Env));
}
WasmEdgePluginTestEnv &getEnv() { return Env; }
private:
WasmEdgePluginTestEnv Env;
};
} // namespace Host
} // namespace WasmEdge
Creation Functions for Modules
Then developers should implement the module creation functions.
Assume that the following implementations are all in the testplugin.cpp
.
#include "testplugin.h"
namespace WasmEdge {
namespace Host {
namespace {
Runtime::Instance::ModuleInstance *
create(const Plugin::PluginModule::ModuleDescriptor *) noexcept {
// There can be several modules in a plug-in. For that, developers should
// implement several `create` functions for each module.
return new WasmEdgePluginTestModule;
}
} // namespace
} // namespace Host
} // namespace WasmEdge
Plug-in Descriptions
For constructing the plug-in, developers should supply the descriptions of this plug-in and the modules.
namespace WasmEdge {
namespace Host {
namespace {
Plugin::Plugin::PluginDescriptor Descriptor{
// Plug-in name. This is the name for searching the plug-in context by the
// `WasmEdge_PluginFind()` C API.
.Name = "wasmedge_plugintest_cpp",
// Plug-in description.
.Description = "",
// Plug-in API version.
.APIVersion = Plugin::Plugin::CurrentAPIVersion,
// Plug-in version.
.Version = {0, 10, 0, 0},
// Module count in this plug-in.
.ModuleCount = 1,
// Pointer to module description array.
.ModuleDescriptions =
// The module descriptor array.
(Plugin::PluginModule::ModuleDescriptor[]){
{
// Module name. This is the name for searching and creating the
// module instance context by the
// `WasmEdge_PluginCreateModule()` C API.
.Name = "wasmedge_plugintest_cpp_module",
// Module description.
.Description = "This is for the plugin tests in WasmEdge.",
// Creation function pointer.
.Create = create,
},
},
// Plug-in options (Work in progress).
.AddOptions = nullptr,
};
} // namespace
} // namespace Host
} // namespace WasmEdge
Plug-in Options
WORK IN PROGRESS. This section is reserved for the feature in the future.
Implement the Plug-in Descriptor Registration
The final step is to implement the Plugin::PluginRegister
initialization with the plug-in descriptor.
namespace WasmEdge {
namespace Host {
Plugin::PluginRegister WasmEdgePluginTestEnv::Register(&Descriptor);
} // namespace Host
} // namespace WasmEdge
Build
To build the plug-in shared library, developers should build in cmake with the WasmEdge source.
Assume that the folder test
is created under the <PATH_TO_WASMEDGE_SOURCE>/plugins
.
Add this line in the <PATH_TO_WASMEDGE_SOURCE>/plugins/CMakeLists.txt
:
add_subdirectory(test)
Copy the testplugin.h
and testplugin.cpp
into the <PATH_TO_WASMEDGE_SOURCE>/plugins/test
directory.
And then edit the file <PATH_TO_WASMEDGE_SOURCE>/plugins/test/CMakeLists.txt
:
wasmedge_add_library(wasmedgePluginTest
SHARED
testplugin.cpp
)
target_compile_options(wasmedgePluginTest
PUBLIC
-DWASMEDGE_PLUGIN
)
target_include_directories(wasmedgePluginTest
PUBLIC
$<TARGET_PROPERTY:wasmedgePlugin,INCLUDE_DIRECTORIES>
${CMAKE_CURRENT_SOURCE_DIR}
)
if(WASMEDGE_LINK_PLUGINS_STATIC)
target_link_libraries(wasmedgePluginTest
PRIVATE
wasmedgeCAPI
)
else()
target_link_libraries(wasmedgePluginTest
PRIVATE
wasmedge_shared
)
endif()
Then you can follow the guide to build from source.