2.12.2021, 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Commit c2348225 authored by Artur Grunau's avatar Artur Grunau
Browse files

Add support for automatic registration of Lua pipelines

As there's currently only one Lua pipeline, we could so far get away
with storing it in the `scripting` directory, giving it a generic name
and registering it manually. However, this approach won't work anymore
once additional Lua pipelines start to be implemented.

To support multiple Lua pipelines, implement a registration mechanism
for scripted pipelines based on PipelineFactory and similar to
PipelineRegistrar. It scans each active module's `pipelines` directory
for Lua pipelines, parses them and generates a registration header that,
when included, registers them with PipelineFactory.

As a result of the above, the test Lua pipeline had to be moved to
`modules/preprocessing/pipelines/` and could be renamed
ResamplingDemoLua.

References #1
parent 0ed0a6bb
......@@ -47,7 +47,7 @@
#include "modules/pipelinefactory.h"
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/scriptedpipelineregistrar.h"
#include "scripting/gen_pipelineregistration.h"
#endif
namespace campvis {
......
......@@ -67,6 +67,61 @@ MACRO(PARSE_HEADER_FOR_PIPELINE FileName)
ENDFOREACH()
ENDMACRO(PARSE_HEADER_FOR_PIPELINE)
MACRO(ADD_SCRIPTED_PIPELINE_REGISTRATION ScriptFile PipelineName)
LIST(APPEND ScriptedPipelineRegistrationScriptFiles ${ScriptFile})
LIST(APPEND ScriptedPipelineRegistrationPipelineNames ${PipelineName})
ENDMACRO(ADD_SCRIPTED_PIPELINE_REGISTRATION)
MACRO(WRITE_SCRIPTED_PIPELINE_REGISTRATION FileName)
MESSAGE(STATUS "* Generating scripted pipeline registration header: ${FileName}")
SET(PipelineRegistrationSource "// WARNING: This file is automatically generated by CMake, do not modify!\n\n" )
LIST(APPEND PipelineRegistrationSource "// Include required headers:\n"
"#include \"scripting/scriptedpipelineregistrar.h\"\n")
LIST(APPEND PipelineRegistrationSource "\nnamespace campvis {\n\tnamespace {\n" )
LIST(APPEND PipelineRegistrationSource "\t\t// Instantiate templated ScriptedPipelineRegistrars to register pipelines.\n" )
WHILE(ScriptedPipelineRegistrationPipelineNames)
LIST(GET ScriptedPipelineRegistrationPipelineNames 0 PipelineName)
LIST(REMOVE_AT ScriptedPipelineRegistrationPipelineNames 0)
LIST(GET ScriptedPipelineRegistrationScriptFiles 0 ScriptFile)
LIST(REMOVE_AT ScriptedPipelineRegistrationScriptFiles 0)
LIST(APPEND PipelineRegistrationSource "\t\tchar ${PipelineName}PipelineId[] = \"${PipelineName}\"\;\n"
"\t\tchar ${PipelineName}PipelineScript[] = \"${ScriptFile}\"\;\n"
"\t\ttemplate class ScriptedPipelineRegistrar"
"<IdentifiableLuaPipeline<${PipelineName}PipelineId>, "
"${PipelineName}PipelineScript>\;\n")
ENDWHILE(ScriptedPipelineRegistrationPipelineNames)
LIST(APPEND PipelineRegistrationSource "\t}\n}\n" )
FILE(WRITE ${FileName} ${PipelineRegistrationSource})
ENDMACRO(WRITE_SCRIPTED_PIPELINE_REGISTRATION)
MACRO(PARSE_SCRIPT_FOR_PIPELINE FileName)
FILE(READ ${FileName} content)
# Build a regex matching pipeline declarations and extracting their names
SET(DeclarationRegex "pipeline = campvis.newPipeline\\(\"([A-Za-z0-9_]+)\"\\)")
# Find all pipeline declarations
STRING(REGEX MATCHALL ${DeclarationRegex} matches ${content})
LIST(LENGTH matches MatchCount)
IF(MatchCount LESS 1)
MESSAGE(WARNING "No pipelines found in script '${FileName}'")
ELSEIF(MatchCount GREATER 1)
MESSAGE(WARNING "Script '${FileName}' defines multiple pipelines; ignoring")
ELSE(MatchCount LESS 1)
# Extract pipeline name and register
STRING(REGEX REPLACE ${DeclarationRegex} "\\1" RESULT ${matches})
ADD_SCRIPTED_PIPELINE_REGISTRATION(${FileName} ${RESULT})
ENDIF(MatchCount LESS 1)
ENDMACRO(PARSE_SCRIPT_FOR_PIPELINE)
MACRO(INCLUDE_MODULE ModuleDirectory ModuleListFile)
STRING(TOUPPER ${ModuleDirectory} ModuleDirectoryUpper)
SET(ThisModDir ${ModulesDir}/${ModuleDirectory})
......
......@@ -252,7 +252,11 @@ namespace campvis {
}
%luacode {
function campvis.newPipeline (o)
function campvis.newPipeline (name, o)
if not name then
error("A name must be provided when creating a new pipeline!")
end
o = o or {} -- create object if user does not provide one
setmetatable(o, {__index = instance})
return o
......
......@@ -3,7 +3,7 @@ require("cvio")
require("vis")
require("preprocessing")
pipeline = campvis.newPipeline()
pipeline = campvis.newPipeline("ResamplingDemoLua")
function pipeline:ctor()
print("I'm being constructed!")
......
# Header with SWIG run-time functions
/swigluarun.h
# Scripted pipeline registration header
/gen_pipelineregistration.h
......@@ -47,6 +47,9 @@ FOREACH(ModName ${CampvisModules})
IF(CAMPVIS_BUILD_MODULE_${ModNameUpper})
LIST(APPEND CampvisBindingDirs "modules/${ModName}")
FILE(GLOB ModLuaPipelines "${CampvisHome}/modules/${ModName}/pipelines/*.lua")
LIST(APPEND CampvisLuaPipelines ${ModLuaPipelines})
ENDIF(CAMPVIS_BUILD_MODULE_${ModNameUpper})
ENDFOREACH(ModName ${CampvisModules})
......@@ -98,6 +101,14 @@ FOREACH(BindingDir ${CampvisBindingDirs})
ENDFOREACH(BindingDir ${CampvisBindingDirs})
# Generate a registration header for Lua pipelines
FOREACH(LuaPipeline ${CampvisLuaPipelines})
PARSE_SCRIPT_FOR_PIPELINE(${LuaPipeline})
ENDFOREACH(LuaPipeline ${CampvisBindingDirs})
WRITE_SCRIPTED_PIPELINE_REGISTRATION("gen_pipelineregistration.h")
# Generate header with SWIG run-time functions
ADD_CUSTOM_COMMAND(
OUTPUT "swigluarun.h"
......
......@@ -12,8 +12,9 @@ extern "C" {
namespace campvis {
LuaPipeline::LuaPipeline(std::string scriptPath, DataContainer* dc)
LuaPipeline::LuaPipeline(const std::string name, std::string scriptPath, DataContainer* dc)
: AutoEvaluationPipeline(dc)
, _name(name)
, _scriptPath(scriptPath)
, _luaVmState(new LuaVmState())
, _pipelineTable(_luaVmState->getGlobalTable()->getTable("pipeline"))
......
......@@ -17,7 +17,7 @@ namespace campvis {
* \param scriptPath Path to the Lua script defining the pipeline
* \param dc DataContainer containing local working set of data for this pipeline
*/
LuaPipeline(const std::string scriptPath, DataContainer* dc);
LuaPipeline(const std::string name, const std::string scriptPath, DataContainer* dc);
/**
* Virtual Destructor
......@@ -25,8 +25,7 @@ namespace campvis {
virtual ~LuaPipeline();
/// \see AbstractPipeline::getName()
virtual const std::string getName() const { return getId(); }
static const std::string getId() { return "LuaPipeline"; }
virtual const std::string getName() const { return _name; }
/// \see AutoEvaluationPipeline::init()
virtual void init();
......@@ -35,6 +34,7 @@ namespace campvis {
virtual void deinit();
protected:
const std::string _name; ///< the name of this pipeline
const std::string _scriptPath; ///< path to the Lua script defining the pipeline
LuaVmState* _luaVmState; ///< Lua VM state used to evaluate the pipeline
std::shared_ptr<LuaTable> _pipelineTable; ///< Pointer to the Lua table associated with the pipeline
......
......@@ -7,9 +7,45 @@
namespace campvis {
static const size_t _factoryId = PipelineFactory::getRef().registerPipeline<LuaPipeline>([] (DataContainer* dc) -> AbstractPipeline* {
return new campvis::LuaPipeline(CAMPVIS_SOURCE_DIR "/scripting/script.lua", dc);
});
/**
* PipelineFactory requires each pipeline to be represented by a separate class whose static
* getId method returns a unique identifier. As all Lua pipelines are instance of one class,
* LuaPipeline, and only differ with respect to the script they execute (which is not known at
* compile time), the following adapter class is needed.
*
* IdentifiableLuaPipeline gets its identifier as a template argument, and is able to return it
* from a static method as a result.
*/
template <const char* id>
class IdentifiableLuaPipeline : public LuaPipeline {
public:
IdentifiableLuaPipeline(const std::string scriptPath, DataContainer* dc) : LuaPipeline(id, scriptPath, dc) {}
static const std::string getId() { return id; }
};
// ================================================================================================
template<typename T, const char* scriptPath>
class ScriptedPipelineRegistrar {
public:
/**
* Static factory method for creating the pipeline of type T.
* \param dc DataContainer for the created pipeline to work on.
* \return A newly created Lua pipeline of type T. Caller has to take ownership of the pointer.
*/
static AbstractPipeline* create(DataContainer* dc) {
return new T(scriptPath, dc);
}
private:
/// static helper field to ensure registration at static initialization time.
static const size_t _factoryId;
};
template<typename T, const char* scriptPath>
const size_t ScriptedPipelineRegistrar<T, scriptPath>::_factoryId =
PipelineFactory::getRef().registerPipeline<T>(&ScriptedPipelineRegistrar<T, scriptPath>::create);
}
#endif // SCRIPTEDPIPELINEREGISTRAR_H__
#include "modules/pipelinefactory.h"
#include "luapipeline.h"
......@@ -7,13 +6,10 @@ using namespace campvis;
int main()
{
PipelineFactory& pipelineFactory = PipelineFactory::getRef();
pipelineFactory.registerPipeline<LuaPipeline>([] (DataContainer* dc) -> AbstractPipeline* {
return new LuaPipeline(CAMPVIS_SOURCE_DIR "/scripting/script.lua", dc);
});
DataContainer* dc = new DataContainer("Test Data Container");
AbstractPipeline* p = pipelineFactory.createPipeline("LuaPipeline", dc);
AbstractPipeline* p = new LuaPipeline("Test Lua Pipeline",
CAMPVIS_SOURCE_DIR "/modules/preprocessing/pipelines/resamplingdemo.lua", dc);
p->init();
p->deinit();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment