I'm doing some high energy physics modelling in C++. I have written code that implements class that score interactions of particles with detector material.
Here is a base class:
class XYZScorer: public virtual AbstractScorer{
public:
XYZScorer(double material_rad_len=-1):
AbstractScorer(material_rad_len){;}
virtual void RegisterHit(const XYZHit hit)=0;
};
Function RegisterHit
registers single interaction between particle and detector. There are many scorers in this program.
Since modeling experimental data is quite hard, I need to be able to add some features to these scorers and I'll need to be able to easily switch these features on and off. I decided to implement these features using a decorator pattern. Here is decorator base class.
template <typename Scorer>
class ScorerDecorator: public virtual XYZScorer{
protected:
Scorer scorer;
public:
ScorerDecorator(Scorer scorer): XYZScorer(scorer.material_rad_len), scorer(scorer){;}
virtual ~ScorerDecorator(){}
virtual void SetEvent(size_t new_incident){
scorer.SetEvent(new_incident);
}
virtual void CloseAfterRunEnd(){
scorer.CloseAfterRunEnd();
}
};
Here are two implementations of these decorators:
template <typename Scorer>
class FixZToFirstInteractionScorer: public virtual ScorerDecorator<Scorer>{
private:
bool fixed_in_this_event=false;
double position_of_first_intraction=0;
public:
FixZToFirstInteractionScorer(Scorer scorer): ScorerDecorator<Scorer>(scorer){}
virtual ~FixZToFirstInteractionScorer(){}
virtual void SetEvent(size_t new_incident){
fixed_in_this_event=false;
ScorerDecorator<Scorer>::SetEvent(new_incident);
}
virtual void RegisterHit(const XYZHit hit){
if(!fixed_in_this_event){
fixed_in_this_event=true;
position_of_first_intraction=hit.z;
}
XYZHit fixed_coords = hit;
fixed_coords.z-=position_of_first_intraction;
this->scorer.RegisterHit(fixed_coords);
}
};
And another one:
template<typename Scorer>
class ApplyEnergyCutsScorer: public virtual ScorerDecorator<Scorer>{
const double e_cut_energy;
public:
ApplyEnergyCutsScorer(Scorer scorer):
ScorerDecorator<Scorer>(scorer), e_cut_energy(GodObject::GetEcut()){}
virtual ~ApplyEnergyCutsScorer(){}
inline bool accept(const XYZHit hit){
double kin_energy = hit.GetParticleKineticEnergy();
if(e_cut_energy < 0){
return true;
}
if (kin_energy <0){
throw new invariant_error("Energy less than zero --- normally it signifies that step wasnt passed to XYZHit");
}
if (kin_energy < e_cut_energy){
return false;
}
return true;
}
virtual void RegisterHit(const XYZHit hit){
if (accept(hit)){
this->scorer.RegisterHit(hit);
}
}
};
So far so good:
- Note that due to template usage and
scorer
not being a pointer there is no virtual function call here at all. This quite important for me because:RegisterHit
function is called for every interaction of any particle with the detector (which is quite often), and having multiple virtual table lookups could be slow (I guess). - I can wrap a class in multiple decorators, and compiler will unwrap and inline everyting (I checked that).
My problem is with instantiating these decorators, wrapping three of them in single method is ugly. Anyways here is the example (from production code):
template<typename Scorer>
std::shared_ptr<XYZScorer> InitializeAnyScorer(
std::shared_ptr<AbstractGeometry> geometry, Scorer scorer, double cut_off_energy){
if (!geometry->IsEnabled()){
return std::shared_ptr<XYZScorer>(0);
}
std::shared_ptr<XYZScorer> result_scorer;
if (!geometry->GetMoveZToFirstInteraction()){
result_scorer = std::shared_ptr<ApplyEnergyCutsScorer<Scorer>>(
new ApplyEnergyCutsScorer<Scorer>(
scorer
)
);
}else{
result_scorer = std::shared_ptr<FixZToFirstInteractionScorer<ApplyEnergyCutsScorer<Scorer>>>(
new FixZToFirstInteractionScorer<ApplyEnergyCutsScorer<Scorer>>(
ApplyEnergyCutsScorer<Scorer>(
scorer
)
)
);
}
return result_scorer;
}
How can I deal with ugliness of instantiating of my decorated scorers?
- Is there any better decorator pattern in C++ that is performed compile time (I want all decorated functions to be inlineable), and applicable to my problem?
- How can my decorated scorers be instantiate in a nicer way? I was considering a variadict template function but I surrendered.
Ideally I'd like to be able to use something similar to:
std::shared_ptr<XYZScorer> ptr
= decorate(scorer, FixZToFirstInteractionScorer, ApplyEnergyCutsScorer);
Non issues
- Both my code and any solution can work in a single thread mode.