Skip to content

Commit

Permalink
Ability to create configurable param from a given external struct
Browse files Browse the repository at this point in the history
This commit provides the new feature of being able to create
configurable param instances from a given external C++ struct.

For example, a C++ struct `struct A { double x; }` can be used to define
a correspondig configurable parameter via

```
class MyConfigParamA : o2::conf::ConfigurableParamPromoter<MyConfigParamA, A> {}
```

This allows to:
- have "template" structs to create multiple params of the same structure
- separate data from configkey functionality
- extract data copies from underlying configurable parameters

The application thereof is demonstrated for GeneratorPythia8Param.

A unit test is added to check the new functionality.
  • Loading branch information
sawenzel committed Nov 25, 2024
1 parent 5c590eb commit fc26161
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 68 deletions.
24 changes: 13 additions & 11 deletions Common/Utils/include/CommonUtils/ConfigurableParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,17 +321,19 @@ class ConfigurableParam
} // end namespace o2

// a helper macro for boilerplate code in parameter classes
#define O2ParamDef(classname, key) \
public: \
classname(TRootIOCtor*) {} \
classname(classname const&) = delete; \
\
private: \
static constexpr char const* const sKey = key; \
static classname sInstance; \
classname() = default; \
template <typename T> \
friend class o2::conf::ConfigurableParamHelper;
#define O2ParamDef(classname, key) \
public: \
classname(TRootIOCtor*) {} \
classname(classname const&) = delete; \
\
private: \
static constexpr char const* const sKey = key; \
static classname sInstance; \
classname() = default; \
template <typename T> \
friend class o2::conf::ConfigurableParamHelper; \
template <typename T, typename P> \
friend class o2::conf::ConfigurableParamPromoter;

// a helper macro to implement necessary symbols in source
#define O2ParamImpl(classname) classname classname::sInstance;
Expand Down
158 changes: 150 additions & 8 deletions Common/Utils/include/CommonUtils/ConfigurableParamHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ class _ParamHelper
{
private:
static std::vector<ParamDataMember>* getDataMembersImpl(std::string const& mainkey, TClass* cl, void*,
std::map<std::string, ConfigurableParam::EParamProvenance> const* provmap);
std::map<std::string, ConfigurableParam::EParamProvenance> const* provmap, size_t virtualoffset);

static void fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void*, boost::property_tree::ptree*,
std::map<std::string, std::pair<std::type_info const&, void*>>*,
EnumRegistry*);
EnumRegistry*, size_t offset);

static void printWarning(std::type_info const&);

static void assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from,
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap);
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t offset);
static void syncCCDBandRegistry(std::string const& mainkey, TClass* cl, void* to, void* from,
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap);
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t offset);

static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector<ParamDataMember> const* members, bool showProv, bool useLogger);
static void printMembersImpl(std::string const& mainkey, std::vector<ParamDataMember> const* members, bool showProv, bool useLogger);
Expand All @@ -65,6 +65,9 @@ class _ParamHelper

template <typename P>
friend class ConfigurableParamHelper;

template <typename Base, typename P>
friend class ConfigurableParamPromoter;
};

// ----------------------------------------------------------------
Expand Down Expand Up @@ -140,7 +143,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam
return nullptr;
}

return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap);
return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 0);
}

// ----------------------------------------------------------------
Expand All @@ -153,7 +156,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam
_ParamHelper::printWarning(typeid(P));
return;
}
_ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry);
_ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 0);
}

// ----------------------------------------------------------------
Expand All @@ -167,7 +170,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam
file->GetObject(getName().c_str(), readback);
if (readback != nullptr) {
_ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)readback,
sValueProvenanceMap);
sValueProvenanceMap, 0);
delete readback;
}
setRegisterMode(true);
Expand All @@ -185,7 +188,146 @@ class ConfigurableParamHelper : virtual public ConfigurableParam
//
setRegisterMode(false);
_ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)externalobj,
sValueProvenanceMap);
sValueProvenanceMap, 0);
setRegisterMode(true);
}

// ----------------------------------------------------------------

void serializeTo(TFile* file) const final
{
file->WriteObjectAny((void*)this, TClass::GetClass(typeid(P)), getName().c_str());
}
};

// Promotes a simple struct Base to a configurable parameter class
// Aka implements all interfaces for a ConfigurableParam P, which shares or
// takes the fields from a Base struct
template <typename P, typename Base>
class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam
{
public:
using ConfigurableParam::ConfigurableParam;

static const P& Instance()
{
return P::sInstance;
}

// extracts a copy of the underlying data struct
Base detach() const
{
static_assert(std::copyable<Base>, "Base type must be copyable.");
return static_cast<Base>(*this);
}

// ----------------------------------------------------------------
std::string getName() const final
{
return P::sKey;
}

// ----------------------------------------------------------------
// get the provenace of the member with given key
EParamProvenance getMemberProvenance(const std::string& key) const final
{
return getProvenance(getName() + '.' + key);
}

// ----------------------------------------------------------------

// one of the key methods, using introspection to print itself
void printKeyValues(bool showProv = true, bool useLogger = false) const final
{
if (!isInitialized()) {
initialize();
}
auto members = getDataMembers();
_ParamHelper::printMembersImpl(getName(), members, showProv, useLogger);
}

//
size_t getHash() const final
{
return _ParamHelper::getHashImpl(getName(), getDataMembers());
}

// ----------------------------------------------------------------

void output(std::ostream& out) const final
{
auto members = getDataMembers();
_ParamHelper::outputMembersImpl(out, getName(), members, true, false);
}

// ----------------------------------------------------------------

// Grab the list of ConfigurableParam data members
// Returns a nullptr if the TClass of the P template class cannot be created.
std::vector<ParamDataMember>* getDataMembers() const
{
// just a helper line to make sure P::sInstance is looked-up
// and that compiler complains about missing static sInstance of type P
// volatile void* ptr = (void*)&P::sInstance;
// static assert on type of sInstance:
static_assert(std::is_same<decltype(P::sInstance), P>::value,
"static instance must of same type as class");

// obtain the TClass for the Base type and delegate further
auto cl = TClass::GetClass(typeid(Base));
if (!cl) {
_ParamHelper::printWarning(typeid(Base));
return nullptr;
}

// we need to put an offset of 8 bytes since internally this is using data members of the Base class
// which doesn't account for the virtual table of P
return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 8);
}

// ----------------------------------------------------------------

// fills the data structures with the initial default values
void putKeyValues(boost::property_tree::ptree* tree) final
{
auto cl = TClass::GetClass(typeid(Base));
if (!cl) {
_ParamHelper::printWarning(typeid(Base));
return;
}
_ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 8);
}

// ----------------------------------------------------------------

void initFrom(TFile* file) final
{
// switch off auto registering since the readback object is
// only a "temporary" singleton
setRegisterMode(false);
P* readback = nullptr;
file->GetObject(getName().c_str(), readback);
if (readback != nullptr) {
_ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)readback,
sValueProvenanceMap, 8);
delete readback;
}
setRegisterMode(true);
}

// ----------------------------------------------------------------

void syncCCDBandRegistry(void* externalobj) final
{
// We may be getting an external copy from CCDB which is passed as externalobj.
// The task of this function is to
// a) update the internal registry with fields coming from CCDB
// but only if keys have not been modified via RT == command line / ini file
// b) update the external object with with fields having RT provenance
//
setRegisterMode(false);
_ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)externalobj,
sValueProvenanceMap, 8);
setRegisterMode(true);
}

Expand Down
30 changes: 15 additions & 15 deletions Common/Utils/src/ConfigurableParamHelper.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,19 @@ std::string asString(TDataMember const& dm, char* pointer)
// potentially other cases to be added here

LOG(error) << "COULD NOT REPRESENT AS STRING";
return nullptr;
return std::string();
}

// ----------------------------------------------------------------------

std::vector<ParamDataMember>* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj,
std::map<std::string, ConfigurableParam::EParamProvenance> const* provmap)
std::map<std::string, ConfigurableParam::EParamProvenance> const* provmap, size_t globaloffset)
{
std::vector<ParamDataMember>* members = new std::vector<ParamDataMember>;

auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) {
auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
auto TS = getSizeOfUnderlyingType(*dm);
char* pointer = ((char*)obj) + dm->GetOffset() + index * TS;
char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset;
const std::string name = getName(dm, index, size);
auto value = asString(*dm, pointer);

Expand Down Expand Up @@ -280,14 +280,14 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt)

void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree,
std::map<std::string, std::pair<std::type_info const&, void*>>* keytostoragemap,
EnumRegistry* enumRegistry)
EnumRegistry* enumRegistry, size_t globaloffset)
{
boost::property_tree::ptree localtree;
auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) {
auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) {
const auto name = getName(dm, index, size);
auto dt = dm->GetDataType();
auto TS = getSizeOfUnderlyingType(*dm);
char* pointer = ((char*)obj) + dm->GetOffset() + index * TS;
char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset;
localtree.put(name, asString(*dm, pointer));

auto key = mainkey + "." + name;
Expand Down Expand Up @@ -355,14 +355,14 @@ bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes
// ----------------------------------------------------------------------

void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from,
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap)
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t globaloffset)
{
auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) {
auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
const auto name = getName(dm, index, size);
auto dt = dm->GetDataType();
auto TS = getSizeOfUnderlyingType(*dm);
char* pointerto = ((char*)to) + dm->GetOffset() + index * TS;
char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS;
char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset;
char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset;

// lambda to update the provenance
auto updateProv = [&mainkey, name, provmap]() {
Expand Down Expand Up @@ -402,14 +402,14 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void*
// ----------------------------------------------------------------------

void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from,
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap)
std::map<std::string, ConfigurableParam::EParamProvenance>* provmap, size_t globaloffset)
{
auto sync = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) {
auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) {
const auto name = getName(dm, index, size);
auto dt = dm->GetDataType();
auto TS = getSizeOfUnderlyingType(*dm);
char* pointerto = ((char*)to) + dm->GetOffset() + index * TS;
char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS;
char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset;
char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset;

// check current provenance
auto key = mainkey + "." + name;
Expand Down
7 changes: 7 additions & 0 deletions Generators/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,15 @@ if(doBuildSimulation)
COMPONENT_NAME Generator
LABELS generator
PUBLIC_LINK_LIBRARIES O2::Generators)

o2_add_test(GeneratorService NAME test_Generator_test_GeneratorPythia8Param
SOURCES test/test_GeneratorPythia8Param.cxx
COMPONENT_NAME Generator
LABELS generator
PUBLIC_LINK_LIBRARIES O2::Generators)
endif()


o2_add_test_root_macro(share/external/tgenerator.C
PUBLIC_LINK_LIBRARIES O2::Generators
LABELS generators)
Expand Down
19 changes: 6 additions & 13 deletions Generators/include/Generators/GeneratorPythia8Param.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,21 @@ namespace eventgen
{

/**
** a parameter class/struct to keep the settings of
** the Pythia8 event generator and
** allow the user to modify them
** a parameter class/struct to configure the settings of
** the GeneratorPythia8 event generator
**/

struct GeneratorPythia8Param : public o2::conf::ConfigurableParamHelper<GeneratorPythia8Param> {
struct Pythia8GenConfig {
std::string config = "";
std::string hooksFileName = "";
std::string hooksFuncName = "";
bool includePartonEvent = false; // whether to keep the event before hadronization
std::string particleFilter = ""; // user particle filter
int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on)
O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8");
};

struct Pythia8GenConfig {
std::string config = "";
std::string hooksFileName = "";
std::string hooksFuncName = "";
bool includePartonEvent = false; // whether to keep the event before hadronization
std::string particleFilter = ""; // user particle filter
int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on)
// construct a configurable param singleton out of the Pythia8GenConfig struct
struct GeneratorPythia8Param : public o2::conf::ConfigurableParamPromoter<GeneratorPythia8Param, Pythia8GenConfig> {
O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8");
};

} // end namespace eventgen
Expand Down
Loading

0 comments on commit fc26161

Please sign in to comment.