Commit c10a469c authored by Nils Erik Krumnack's avatar Nils Erik Krumnack Committed by Johannes Elmsheuser
Browse files

change handling of sub-tools, allow configuring ToolHandleArray properties

Originally the goal was just to allow handling `ToolHandleArray`
properties, but it turned out to be a lot easier if subtools are
represented by `AsgToolConfig` in turn.
parent 597a77d2
......@@ -10,6 +10,7 @@
#define ASG_TOOLS__I_UNIT_TEST_TOOL2_H
#include <AsgTools/IAsgTool.h>
#include <AsgTools/ToolHandleArray.h>
namespace asg
{
......@@ -41,6 +42,11 @@ namespace asg
public:
virtual bool
wasUserConfigured (const std::string& handleName) const = 0;
/// \brief get the tool handle array
public:
virtual const ToolHandleArray<IUnitTestTool1>&
getArray () const = 0;
};
}
......
......@@ -48,6 +48,10 @@ namespace asg
virtual bool
wasUserConfigured (const std::string& handleName) const override;
public:
virtual const ToolHandleArray<IUnitTestTool1>&
getArray () const override;
public:
ToolHandle<IUnitTestTool1> m_regPublicHandle;
......@@ -60,6 +64,10 @@ namespace asg
public:
AnaToolHandle<IUnitTestTool1> m_anaPrivateHandle;
public:
ToolHandleArray<IUnitTestTool1> m_regPrivateArray {
this, "regPrivateArray", {}, "array of private tools"};
private:
bool m_wasUserConfiguredPublic = false;
......
......@@ -52,6 +52,9 @@ namespace asg
ANA_CHECK (m_anaPrivateHandle.setProperty ("propertyInt", 222));
ANA_CHECK (m_anaPrivateHandle.initialize ());
for (auto& handle : m_regPrivateArray)
ANA_CHECK (handle.retrieve());
return StatusCode::SUCCESS;
}
......@@ -110,4 +113,12 @@ namespace asg
return m_wasUserConfiguredPrivate;
throw std::runtime_error ("unknown handle: " + handleName);
}
const ToolHandleArray<IUnitTestTool1>& UnitTestTool2 ::
getArray () const
{
return m_regPrivateArray;
}
}
......@@ -184,6 +184,47 @@ namespace asg
ASSERT_SUCCESS (config2.makeTool (tool2, cleanup2));
EXPECT_EQ ("ToolSvc." + name1 + ".myPrivateTool", tool2->name());
}
TEST (AsgToolConfigTest, emptyArray)
{
const std::string name = makeUniqueName();
AsgToolConfig mainConfig ("asg::UnitTestTool2/" + name);
std::shared_ptr<void> cleanup;
ToolHandle<IUnitTestTool2> tool;
ASSERT_SUCCESS (mainConfig.makeTool (tool, cleanup));
EXPECT_EQ ("ToolSvc." + name, tool->name());
ASSERT_EQ (0u, tool->getArray().size());
}
TEST (AsgToolConfigTest, fillArray)
{
const std::string name = makeUniqueName();
AsgToolConfig mainConfig ("asg::UnitTestTool2/" + name);
{
auto subtool = mainConfig.createPrivateToolInArray ("regPrivateArray", "asg::UnitTestTool1");
ANA_MSG_INFO ("subtool = " << subtool);
ASSERT_SUCCESS (mainConfig.setProperty (subtool + ".propertyInt", 19));
}
{
AsgToolConfig subconfig ("asg::UnitTestTool1");
ANA_CHECK_THROW (subconfig.setProperty ("propertyInt", 22));
auto subtool = mainConfig.addPrivateToolInArray ("regPrivateArray", subconfig);
ANA_MSG_INFO ("subtool = " << subtool);
}
std::shared_ptr<void> cleanup;
ToolHandle<IUnitTestTool2> tool;
ASSERT_SUCCESS (mainConfig.makeTool (tool, cleanup));
EXPECT_EQ ("ToolSvc." + name, tool->name());
ASSERT_EQ (2u, tool->getArray().size());
EXPECT_EQ (19, tool->getArray()[0]->getPropertyInt());
EXPECT_EQ (22, tool->getArray()[1]->getPropertyInt());
}
}
ATLAS_GOOGLE_TEST_MAIN
......@@ -170,7 +170,36 @@ namespace asg
/// out of memory II
public:
StatusCode addPrivateTool (const std::string& name,
const AsgToolConfig& toolConfig);
AsgToolConfig toolConfig);
/// \brief the array version of \ref createPrivateTool
///
/// This returns the actual name of the tool to allow setting
/// properties on it.
///
/// \par Guarantee
/// basic
/// \par Failures
/// out of memory II
public:
std::string createPrivateToolInArray (const std::string& name,
const std::string& toolType);
/// \brief the array version of \ref addPrivateTool
///
/// This will ignore the name set in `toolConfig` and use whatever
/// `name` is given instead.
///
/// \par Guarantee
/// basic
/// \par Failures
/// out of memory II
public:
std::string addPrivateToolInArray (const std::string& name,
AsgToolConfig toolConfig);
#ifdef XAOD_STANDALONE
......@@ -237,20 +266,22 @@ namespace asg
// private interface
//
/// \brief the value of \ref type
private:
/// \brief the value of \ref type
std::string m_type;
/// \brief the value of \ref name
private:
std::string m_name;
/// \brief the map of private tools to create
private:
std::map<std::string,std::string> m_privateTools;
/// \brief the map of (private) tools to create
std::map<std::string,std::tuple<AsgToolConfig,std::string>> m_privateTools;
/// \brief the map of (private) tool handle arrays to manage, and
/// the tools they contain
std::map<std::string,std::vector<decltype(m_privateTools.cbegin())>> m_toolArrays;
/// \brief the map of property values
private:
std::map<std::string,std::string> m_propertyValues;
......@@ -259,11 +290,29 @@ namespace asg
/// strong
/// \par Failures
/// type or name doesn't have proper format
private:
StatusCode checkTypeName (bool nestedNames) const;
/// \brief access the configuration for the given subtool
/// \par Guarantee
/// strong
/// \par Failures
/// unknown subtool
/// \{
struct AccessSubtoolData final
{
AsgToolConfig *config {nullptr};
std::string prefix;
std::string name;
};
AccessSubtoolData accessSubtool (const std::string& name,
std::size_t split);
/// \}
};
}
#include "AsgComponentConfig.icc"
#include <AsgTools/AsgToolConfig.h>
#endif
......@@ -150,7 +150,7 @@ namespace asg
ANA_MSG_ERROR ("type \"" << type << "\" does not match format expression");
return StatusCode::FAILURE;
}
std::regex nameExpr ("[A-Za-z_][A-Za-z0-9_]*((\\.|::)[A-Za-z_][A-Za-z0-9_]*)*");
std::regex nameExpr ("[A-Za-z_][A-Za-z0-9_]*((\\.|::)[A-Za-z_][A-Za-z0-9_]*)*(@[0-9]+)?");
if (!std::regex_match (name, nameExpr))
{
ANA_MSG_ERROR ("name \"" << name << "\" does not match format expression");
......
......@@ -36,6 +36,21 @@
namespace asg
{
namespace
{
/// make the name of a private tool in an array
///
/// I'm not sure if there is a specific naming convention for
/// tools in tool arrays, and I'm not sure if it matters. So this
/// allows to change the naming scheme of private tools in arrays
/// at will, if needed.
std::string makeArrayName (const std::string& name, std::size_t index)
{
return name + "@" + std::to_string (index);
}
}
AsgComponentConfig ::
AsgComponentConfig (const std::string& val_typeAndName)
{
......@@ -117,7 +132,15 @@ namespace asg
setPropertyFromString (const std::string& name,
const std::string& value)
{
m_propertyValues[name] = value;
auto split = name.find ('.');
if (split == std::string::npos)
{
m_propertyValues[name] = value;
} else
{
auto subtool = accessSubtool (name, split);
subtool.config->setPropertyFromString (subtool.name, value);
}
}
......@@ -126,8 +149,62 @@ namespace asg
createPrivateTool (const std::string& name,
const std::string& toolType)
{
m_privateTools[name] = toolType;
return StatusCode::SUCCESS;
return addPrivateTool (name, AsgToolConfig (toolType + "/" + name));
}
std::string AsgComponentConfig ::
createPrivateToolInArray (const std::string& name,
const std::string& toolType)
{
return addPrivateToolInArray (name, AsgToolConfig (toolType + "/" + name));
}
StatusCode AsgComponentConfig ::
addPrivateTool (const std::string& name,
AsgToolConfig toolConfig)
{
using namespace msgComponentConfig;
auto split = name.find ('.');
if (split == std::string::npos)
{
toolConfig.setName (name);
m_privateTools[name] = std::make_tuple (std::move (toolConfig), "");
return StatusCode::SUCCESS;
} else
{
auto subtool = accessSubtool (name, split);
return subtool.config->addPrivateTool (subtool.name, std::move (toolConfig));
}
}
std::string AsgComponentConfig ::
addPrivateToolInArray (const std::string& name,
AsgToolConfig toolConfig)
{
using namespace msgComponentConfig;
auto split = name.find ('.');
if (split == std::string::npos)
{
auto& arrayData = m_toolArrays[name];
auto myname = makeArrayName (name, arrayData.size());
toolConfig.setName (myname);
auto emplace_result = m_privateTools.emplace (myname, std::make_tuple (std::move (toolConfig), name));
arrayData.push_back (emplace_result.first);
return myname;
} else
{
auto subtool = accessSubtool (name, split);
return subtool.prefix + subtool.config
->addPrivateToolInArray (subtool.name, std::move (toolConfig));
}
}
......@@ -145,9 +222,9 @@ namespace asg
}
std::regex nameExpr;
if (nestedNames)
nameExpr = std::regex ("[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*");
nameExpr = std::regex ("[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*(@[0-9]+)?");
else
nameExpr = std::regex ("[A-Za-z_][A-Za-z0-9_]*");
nameExpr = std::regex ("[A-Za-z_][A-Za-z0-9_]*(@[0-9]+)?");
if (!std::regex_match (m_name, nameExpr))
{
ANA_MSG_ERROR ("name \"" << m_name << "\" does not match format expression");
......@@ -158,18 +235,17 @@ namespace asg
StatusCode AsgComponentConfig ::
addPrivateTool (const std::string& name,
const AsgToolConfig& toolConfig)
AsgComponentConfig::AccessSubtoolData AsgComponentConfig ::
accessSubtool (const std::string& name, std::size_t split)
{
using namespace msgComponentConfig;
ANA_CHECK (createPrivateTool (name, toolConfig.type()));
for (const auto& subtool : toolConfig.m_privateTools)
ANA_CHECK (createPrivateTool (name + "." + subtool.first, subtool.second));
for (const auto& property : toolConfig.m_propertyValues)
setPropertyFromString (name + "." + property.first, property.second);
return StatusCode::SUCCESS;
AccessSubtoolData result;
auto subtool = m_privateTools.find (name.substr (0, split));
if (subtool == m_privateTools.end())
throw std::runtime_error ("trying to access unknown private tool: " + name.substr (0, split));
result.config = &std::get<0>(subtool->second);
result.prefix = name.substr (0, split + 1);
result.name = name.substr (split + 1);
return result;
}
......@@ -177,121 +253,6 @@ namespace asg
#ifdef XAOD_STANDALONE
namespace
{
/// \brief count the number of separators in a tool name
/// \par Guarantee
/// no-fail
std::size_t countSeparators (const std::string& name)
{
std::size_t result {!name.empty()};
for (char ch : name)
{
if (ch == '.')
result += 1;
}
return result;
}
/// \brief the map of all known components
struct ComponentMap final
{
/// \brief the list of tools we created/know about
std::map<std::string,AsgComponent*> m_components;
/// \brief the tool cleanup list
std::list<std::shared_ptr<void> > m_cleanup;
/// \brief set a property on the component or a sub-tool
/// \par Guarantee
/// basic
/// \par Failures
/// could not set property\n
/// property not found\n
/// sub-tool not found
StatusCode setProperty (const std::string& name,
const std::string& value)
{
using namespace msgComponentConfig;
std::string componentName;
std::string propertyName;
const auto split = name.rfind (".");
if (split == std::string::npos)
{
propertyName = name;
} else
{
componentName = name.substr (0, split);
propertyName = name.substr (split+1);
}
const auto component = m_components.find (componentName);
if (component == m_components.end())
{
ANA_MSG_ERROR ("trying to set property \"" << propertyName << "\" on component \"" << componentName << "\" which has not been configured");
return StatusCode::FAILURE;
}
return component->second->setProperty (propertyName, value);
}
/// \brief initialize all the tools
///
/// need to initialize the tools inside out, i.e. sub-sub-tools
/// before sub-tools, etc. and then set them as properties on
/// their parents
/// \par Guarantee
/// basic
/// \par Failures
/// out of memory II\n
/// tool initialization failures
StatusCode initializeTools ()
{
using namespace msgComponentConfig;
std::vector<std::pair<std::string,AsgTool*> > sortedTools;
for (auto& component : m_components)
{
// we skip the top-level component which will be initialized
// by the component specific wrapper
if (!component.first.empty())
{
AsgTool *tool = dynamic_cast<AsgTool*>(component.second);
if (tool == nullptr)
{
ANA_MSG_ERROR ("configured non-tool component as subtool: " << component.first);
return StatusCode::FAILURE;
}
sortedTools.emplace_back (component.first, tool);
}
}
std::sort (sortedTools.begin(), sortedTools.end(), [] (auto& a, auto& b) {
const std::size_t levela = countSeparators (a.first);
const std::size_t levelb = countSeparators (b.first);
if (levela > levelb) return true;
if (levela < levelb) return false;
return a.first < b.first;});
for (const auto& tool : sortedTools)
{
ANA_CHECK (tool.second->initialize());
// using that a ToolHandle initialized with a tool name will
// retrieve that tool, not sure if that is the best strategy
ANA_CHECK (setProperty (tool.first, tool.second->name()));
}
return StatusCode::SUCCESS;
}
};
StatusCode createComponent (std::unique_ptr<AsgComponent>& component,
const std::string& type,
const std::string& name,
......@@ -321,23 +282,6 @@ namespace asg
return StatusCode::SUCCESS;
}
StatusCode createTool (const std::string& type,
const std::string& name,
std::shared_ptr<asg::AsgTool>& tool)
{
using namespace msgComponentConfig;
// ideally we move the makeToolRootCore out of the detail
// namespace, but for now I just want to make this work.
asg::AsgTool *rawTool = nullptr;
if (!asg::detail::makeToolRootCore (type, name, rawTool).isSuccess())
return StatusCode::FAILURE;
tool.reset (rawTool);
return StatusCode::SUCCESS;
}
}
......@@ -353,31 +297,43 @@ namespace asg
std::string name = prefix + m_name;
ComponentMap componentMap;
if (!createComponent (component, m_type, name, newCommand).isSuccess())
return StatusCode::FAILURE;
componentMap.m_components.insert (std::make_pair ("", component.get()));
for (auto& toolInfo : m_privateTools)
{
std::shared_ptr<asg::AsgTool> tool;
if (!createTool (toolInfo.second, name + "." + toolInfo.first, tool).isSuccess())
ToolHandle<AsgTool> th (toolInfo.first, component.get());
std::shared_ptr<void> mycleanup;
if (std::get<0>(toolInfo.second).makeTool (th, mycleanup).isFailure())
{
ANA_MSG_ERROR ("failed to create subtool \"" << toolInfo.first << "\" on component \"" << component->name() << "\"");
return StatusCode::FAILURE;
componentMap.m_cleanup.push_front (tool);
componentMap.m_components.insert (std::make_pair (toolInfo.first, tool.get()));
}
component->addCleanup (mycleanup);
if (std::get<1>(toolInfo.second).empty())
{
if (component->setProperty (toolInfo.first, th->name()).isFailure())
{
ANA_MSG_ERROR ("failed to set ToolHandle property \"" << toolInfo.first << "\" on component \"" << component->name() << "\"");
return StatusCode::FAILURE;
}
}
}
for (auto& property : m_propertyValues)
for (const auto& toolArray : m_toolArrays)
{
ANA_CHECK (componentMap.setProperty (property.first, property.second));
std::vector<std::string> valueArray;
for (const auto& tool : toolArray.second)
valueArray.emplace_back (component->name() + "." + tool->first);
std::string valueString;
ANA_CHECK (asg::detail::GetCastStringHelper<std::vector<std::string>>::get (valueArray, valueString));
ANA_CHECK (component->setProperty (toolArray.first, valueString));
}
if (!componentMap.initializeTools ().isSuccess())
return StatusCode::FAILURE;
for (auto& cleanup : componentMap.m_cleanup)
component->addCleanup (cleanup);
for (auto& property : m_propertyValues)
{
ANA_CHECK (component->setProperty (property.first, property.second));
}
ANA_MSG_DEBUG ("Created component of type " << m_type);
return StatusCode::SUCCESS;
......@@ -402,10 +358,22 @@ namespace asg
for (const auto& tool : m_privateTools)
{
std::string toolPath = prefix + m_name + "." + tool.first;
const auto split = toolPath.rfind ('.');
std::string toolName = toolPath.substr (split+1);
joSvc->set (toolPath, tool.second + "/" + toolName);
ANA_CHECK (std::get<0>(tool.second).configureComponentExpert (prefix + m_name + ".", true));
if (std::get<1>(tool.second).empty())
{
std::string toolPath = prefix + m_name + "." + tool.first;
joSvc->set (toolPath, std::get<0>(tool.second).typeAndName());
}
}
for (const auto& toolArray : m_toolArrays)
{
std::vector<std::string> valueArray;
for (const auto& tool : toolArray.second)