Skip to content

Commit c13d00c

Browse files
committed
add TypedStringObjectMethodCaller functor and parser components for generic object's method caller
1 parent b96fd02 commit c13d00c

File tree

6 files changed

+387
-0
lines changed

6 files changed

+387
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#ifndef CommonTools_Utils_TypedStringObjectMethodCaller_h
2+
#define CommonTools_Utils_TypedStringObjectMethodCaller_h
3+
/* \class TypedStringObjectMethodCaller
4+
*
5+
* Object's method (or a chain of methods) caller functor with generic return-type, specified by string expression
6+
*
7+
*/
8+
9+
#include "FWCore/Utilities/interface/EDMException.h"
10+
#include "CommonTools/Utils/interface/parser/Exception.h"
11+
#include "CommonTools/Utils/interface/parser/MethodChain.h"
12+
#include "CommonTools/Utils/interface/parser/MethodChainGrammar.h"
13+
#include "FWCore/Reflection/interface/ObjectWithDict.h"
14+
15+
template <typename T, typename R, bool DefaultLazyness = false>
16+
struct TypedStringObjectMethodCaller {
17+
TypedStringObjectMethodCaller(const std::string expr, bool lazy = DefaultLazyness) : type_(typeid(T)) {
18+
using namespace boost::spirit::classic;
19+
reco::parser::MethodChainGrammar grammar(methodchain_, type_, lazy);
20+
const char* startingFrom = expr.c_str();
21+
try {
22+
if (!parse(startingFrom, grammar >> end_p, space_p).full) {
23+
throw edm::Exception(edm::errors::Configuration, "failed to parse \"" + expr + "\"");
24+
}
25+
} catch (reco::parser::BaseException& e) {
26+
throw edm::Exception(edm::errors::Configuration)
27+
<< "MethodChainGrammer parse error:" << reco::parser::baseExceptionWhat(e) << " (char " << e.where - startingFrom << ")\n";
28+
}
29+
}
30+
31+
R operator()(const T &t) const {
32+
edm::ObjectWithDict o(type_, const_cast<T *>(&t));
33+
edm::ObjectWithDict ret = methodchain_->value(o);
34+
return *static_cast<R*>(ret.address());
35+
}
36+
37+
private:
38+
reco::parser::MethodChainPtr methodchain_;
39+
edm::TypeWithDict type_;
40+
};
41+
42+
#endif
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef CommonTools_Utils_MethodChain_h
2+
#define CommonTools_Utils_MethodChain_h
3+
4+
/* \class reco::parser::MethodChain
5+
*
6+
* Chain of methods
7+
* Based on ExpressionBase and ExpressionVar, but remove final conversion to double
8+
*
9+
*/
10+
11+
#include "CommonTools/Utils/interface/parser/MethodInvoker.h"
12+
#include "CommonTools/Utils/interface/TypeCode.h"
13+
14+
#include <vector>
15+
#include <oneapi/tbb/concurrent_queue.h>
16+
17+
namespace reco {
18+
namespace parser {
19+
20+
/// Based on Expression, but its value method returns an edm::ObjectWithDict instead of a double
21+
class MethodChainBase {
22+
public: // Public Methods
23+
virtual ~MethodChainBase() {}
24+
virtual edm::ObjectWithDict value(const edm::ObjectWithDict&) const = 0;
25+
};
26+
27+
/// Shared ptr to MethodChainBase
28+
typedef std::shared_ptr<MethodChainBase> MethodChainPtr;
29+
30+
/// Evaluate an object's method or datamember (or chain of them)
31+
class MethodChain : public MethodChainBase {
32+
private: // Private Data Members
33+
std::vector<MethodInvoker> methods_;
34+
using Objects = std::vector<std::pair<edm::ObjectWithDict, bool>>;
35+
mutable oneapi::tbb::concurrent_queue<Objects> objectsCache_;
36+
37+
private: // Private Methods
38+
[[nodiscard]] Objects initObjects_() const;
39+
40+
Objects borrowObjects() const;
41+
void returnObjects(Objects&&) const;
42+
43+
public: // Public Static Methods
44+
45+
/// allocate an object to hold the result of a given member (if needed)
46+
/// this method is used also from the LazyInvoker code
47+
/// returns true if objects returned from this will require a destructor
48+
static bool makeStorage(edm::ObjectWithDict& obj, const edm::TypeWithDict& retType);
49+
50+
/// delete an objecty, if needed
51+
/// this method is used also from the LazyInvoker code
52+
static void delStorage(edm::ObjectWithDict&);
53+
54+
public: // Public Methods
55+
MethodChain(const std::vector<MethodInvoker>& methods);
56+
MethodChain(const MethodChain&);
57+
~MethodChain();
58+
edm::ObjectWithDict value(const edm::ObjectWithDict&) const override;
59+
};
60+
61+
/// Same as MethodChain but with lazy resolution of object methods
62+
/// using the dynamic type of the object, and not the one fixed at compile time
63+
class LazyMethodChain : public MethodChainBase {
64+
private: // Private Data Members
65+
std::vector<LazyInvoker> methods_;
66+
67+
public:
68+
LazyMethodChain(const std::vector<LazyInvoker>& methods);
69+
~LazyMethodChain() override;
70+
edm::ObjectWithDict value(const edm::ObjectWithDict&) const override;
71+
};
72+
73+
} // namespace parser
74+
} // namespace reco
75+
76+
#endif // CommonTools_Utils_MethodChain_h
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef CommonTools_Utils_MethodChainGrammar_h
2+
#define CommonTools_Utils_MethodChainGrammar_h
3+
/* \class MethodChainGrammer
4+
*
5+
* subset grammar of the full Grammer (CommonTools/Utils/interface/parser/Grammar.h), allowing only a chain of methods
6+
*
7+
*/
8+
9+
#include "boost/spirit/include/classic_core.hpp"
10+
#include "boost/spirit/include/classic_grammar_def.hpp"
11+
#include "boost/spirit/include/classic_chset.hpp"
12+
#include "FWCore/Utilities/interface/EDMException.h"
13+
#include "FWCore/Reflection/interface/ObjectWithDict.h"
14+
#include "CommonTools/Utils/interface/parser/MethodChain.h"
15+
#include "CommonTools/Utils/interface/parser/MethodChainSetter.h"
16+
#include "CommonTools/Utils/interface/parser/MethodSetter.h"
17+
#include "CommonTools/Utils/interface/parser/MethodArgumentSetter.h"
18+
#include "CommonTools/Utils/interface/parser/MethodInvoker.h"
19+
#include "CommonTools/Utils/interface/parser/MethodStack.h"
20+
#include "CommonTools/Utils/interface/parser/TypeStack.h"
21+
#include "CommonTools/Utils/interface/parser/MethodArgumentStack.h"
22+
#include "CommonTools/Utils/interface/parser/AnyMethodArgument.h"
23+
#include "CommonTools/Utils/interface/parser/Exception.h"
24+
25+
namespace reco {
26+
namespace parser {
27+
struct MethodChainGrammar : public boost::spirit::classic::grammar<MethodChainGrammar> {
28+
MethodChainPtr *methchain_;
29+
bool lazy_;
30+
mutable MethodStack methStack;
31+
mutable LazyMethodStack lazyMethStack;
32+
mutable MethodArgumentStack methArgStack;
33+
mutable TypeStack typeStack;
34+
35+
MethodChainGrammar(MethodChainPtr &methchain, const edm::TypeWithDict& iType, bool lazy = false)
36+
: methchain_(&methchain), lazy_(lazy) {
37+
typeStack.push_back(iType);
38+
}
39+
40+
template <typename ScannerT>
41+
struct definition {
42+
typedef boost::spirit::classic::rule<ScannerT> rule;
43+
rule metharg, method, arrayAccess, methodchain;
44+
definition(const MethodChainGrammar& self) {
45+
using namespace boost::spirit::classic;
46+
47+
MethodArgumentSetter methodArg_s(self.methArgStack);
48+
MethodSetter method_s(self.methStack, self.lazyMethStack, self.typeStack, self.methArgStack, self.lazy_);
49+
MethodChainSetter methodchain_s(*self.methchain_, self.methStack, self.lazyMethStack, self.typeStack);
50+
51+
BOOST_SPIRIT_DEBUG_RULE(methodchain);
52+
BOOST_SPIRIT_DEBUG_RULE(arrayAccess);
53+
BOOST_SPIRIT_DEBUG_RULE(method);
54+
BOOST_SPIRIT_DEBUG_RULE(metharg);
55+
56+
boost::spirit::classic::assertion<SyntaxErrors> expectParenthesis(kMissingClosingParenthesis);
57+
boost::spirit::classic::assertion<SyntaxErrors> expect(kSyntaxError);
58+
59+
metharg = (strict_real_p[methodArg_s]) | (int_p[methodArg_s]) |
60+
(ch_p('"') >> *(~ch_p('"')) >> ch_p('"'))[methodArg_s] |
61+
(ch_p('\'') >> *(~ch_p('\'')) >> ch_p('\''))[methodArg_s];
62+
method = // alnum_p doesn't accept underscores, so we use chset<>; lexeme_d needed to avoid whitespace skipping within method names
63+
(lexeme_d[alpha_p >> *chset<>("a-zA-Z0-9_")] >> ch_p('(') >> metharg >> *(ch_p(',') >> metharg) >>
64+
expectParenthesis(ch_p(')')))[method_s] |
65+
((lexeme_d[alpha_p >> *chset<>("a-zA-Z0-9_")])[method_s] >> !(ch_p('(') >> ch_p(')')));
66+
arrayAccess = (ch_p('[') >> metharg >> *(ch_p(',') >> metharg) >> expectParenthesis(ch_p(']')))[method_s];
67+
methodchain = (method >> *(arrayAccess | (ch_p('.') >> expect(method))))[methodchain_s];
68+
}
69+
70+
rule const& start() const { return methodchain; }
71+
};
72+
};
73+
}
74+
}
75+
76+
#endif
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#ifndef CommonTools_Utils_MethodChainSetter_h
2+
#define CommonTools_Utils_MethodChainSetter_h
3+
/* \class reco::parser::MethodChainSetter
4+
*
5+
* Method Chain setter, used to construct MethodChain when parsing with boost::spirit
6+
*
7+
*/
8+
#include "CommonTools/Utils/interface/parser/MethodChain.h"
9+
#include "CommonTools/Utils/interface/parser/MethodStack.h"
10+
#include "CommonTools/Utils/interface/parser/TypeStack.h"
11+
12+
#include <iostream>
13+
14+
namespace reco {
15+
namespace parser {
16+
struct MethodChainSetter {
17+
MethodChainSetter(MethodChainPtr &methchain,
18+
MethodStack &methStack,
19+
LazyMethodStack &lazyMethStack,
20+
TypeStack &typeStack)
21+
: methchain_(methchain), methStack_(methStack), lazyMethStack_(lazyMethStack), typeStack_(typeStack) {}
22+
void operator()(const char *, const char *) const;
23+
24+
private:
25+
void push(const char *, const char *) const;
26+
void lazyPush(const char *, const char *) const;
27+
28+
MethodChainPtr &methchain_;
29+
MethodStack &methStack_;
30+
LazyMethodStack &lazyMethStack_;
31+
TypeStack &typeStack_;
32+
};
33+
} // namespace parser
34+
} // namespace reco
35+
36+
#endif
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include "CommonTools/Utils/interface/parser/MethodChain.h"
2+
#include "CommonTools/Utils/interface/parser/MethodInvoker.h"
3+
4+
#include "FWCore/Reflection/interface/ObjectWithDict.h"
5+
#include "FWCore/Reflection/interface/FunctionWithDict.h"
6+
#include "FWCore/Reflection/interface/MemberWithDict.h"
7+
#include "FWCore/Reflection/interface/TypeWithDict.h"
8+
9+
#include <cassert>
10+
#include <map>
11+
12+
using namespace reco::parser;
13+
using namespace std;
14+
15+
MethodChain::Objects MethodChain::initObjects_() const {
16+
Objects objects(methods_.size(), {edm::ObjectWithDict(), false});
17+
assert(objects.size() == methods_.size());
18+
auto IO = objects.begin();
19+
for (auto const& method : methods_) {
20+
if (method.isFunction()) {
21+
edm::TypeWithDict retType = method.method().finalReturnType();
22+
IO->second = makeStorage(IO->first, retType);
23+
} else {
24+
*IO = {edm::ObjectWithDict(), false};
25+
}
26+
++IO;
27+
}
28+
return objects;
29+
}
30+
31+
MethodChain::MethodChain(const vector<MethodInvoker>& methods)
32+
: methods_(methods) {
33+
returnObjects(initObjects_());
34+
}
35+
36+
MethodChain::MethodChain(const MethodChain& rhs) : methods_(rhs.methods_) {
37+
returnObjects(initObjects_());
38+
}
39+
40+
MethodChain::Objects MethodChain::borrowObjects() const {
41+
Objects objects;
42+
if (objectsCache_.try_pop(objects)) {
43+
return objects;
44+
}
45+
return initObjects_();
46+
}
47+
48+
void MethodChain::returnObjects(Objects&& iOb) const { objectsCache_.push(std::move(iOb)); }
49+
50+
MethodChain::~MethodChain() {
51+
Objects objects;
52+
while (objectsCache_.try_pop(objects)) {
53+
for (auto& o : objects) {
54+
delStorage(o.first);
55+
}
56+
}
57+
}
58+
59+
void MethodChain::delStorage(edm::ObjectWithDict& obj) {
60+
if (!obj.address()) {
61+
return;
62+
}
63+
if (obj.typeOf().isPointer() || obj.typeOf().isReference()) {
64+
// just delete a void*, as that's what it was
65+
void** p = static_cast<void**>(obj.address());
66+
delete p;
67+
} else {
68+
//std::cout << "Calling Destruct on a " <<
69+
// obj.typeOf().qualifiedName() << std::endl;
70+
obj.typeOf().deallocate(obj.address());
71+
}
72+
}
73+
74+
bool MethodChain::makeStorage(edm::ObjectWithDict& obj, const edm::TypeWithDict& retType) {
75+
static const edm::TypeWithDict tVoid(edm::TypeWithDict::byName("void"));
76+
bool ret = false;
77+
if (retType == tVoid) {
78+
obj = edm::ObjectWithDict::byType(tVoid);
79+
} else if (retType.isPointer() || retType.isReference()) {
80+
// in this case, I have to allocate a void*, not an object!
81+
obj = edm::ObjectWithDict(retType, new void*);
82+
} else {
83+
obj = edm::ObjectWithDict(retType, retType.allocate());
84+
ret = retType.isClass();
85+
//std::cout << "MethodChain: reserved memory at " << obj.address() <<
86+
// " for a " << retType.qualifiedName() << " returned by " <<
87+
// member.name() << std::endl;
88+
}
89+
return ret;
90+
}
91+
92+
edm::ObjectWithDict MethodChain::value(const edm::ObjectWithDict& obj) const {
93+
edm::ObjectWithDict val(obj);
94+
auto objects = borrowObjects();
95+
auto IO = objects.begin();
96+
for (auto& m : methods_) {
97+
val = m.invoke(val, IO->first);
98+
++IO;
99+
}
100+
for (auto RI = objects.rbegin(), RE = objects.rend(); RI != RE; ++RI) {
101+
if (RI->second) {
102+
RI->first.destruct(false);
103+
}
104+
}
105+
returnObjects(std::move(objects));
106+
return val;
107+
}
108+
109+
LazyMethodChain::LazyMethodChain(const std::vector<LazyInvoker>& methods) : methods_(methods) {}
110+
111+
LazyMethodChain::~LazyMethodChain() {}
112+
113+
edm::ObjectWithDict LazyMethodChain::value(const edm::ObjectWithDict& o) const {
114+
edm::ObjectWithDict val = o;
115+
std::vector<StorageManager> storage;
116+
storage.reserve(methods_.size());
117+
118+
std::vector<LazyInvoker>::const_iterator I = methods_.begin();
119+
std::vector<LazyInvoker>::const_iterator E = methods_.end();
120+
for (; I < E; ++I) {
121+
val = I->invoke(val, storage);
122+
}
123+
while (not storage.empty()) {
124+
storage.pop_back();
125+
}
126+
return val;
127+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "CommonTools/Utils/interface/parser/MethodChainSetter.h"
2+
#include "CommonTools/Utils/interface/parser/MethodChain.h"
3+
#include "CommonTools/Utils/interface/returnType.h"
4+
#include "CommonTools/Utils/interface/parser/Exception.h"
5+
#include <string>
6+
7+
using namespace reco::parser;
8+
using namespace std;
9+
10+
void MethodChainSetter::operator()(const char *begin, const char *end) const {
11+
//std::cerr << "MethodChainSetter: Pushed [" << std::string(begin,end) << "]" << std::endl;
12+
if (!methStack_.empty())
13+
push(begin, end);
14+
else if (!lazyMethStack_.empty())
15+
lazyPush(begin, end);
16+
else
17+
throw Exception(begin) << " Expression didn't parse neither hastily nor lazyly. This must not happen.\n";
18+
}
19+
20+
void MethodChainSetter::push(const char *begin, const char *end) const {
21+
methchain_ = std::shared_ptr<MethodChainBase>(new MethodChain(methStack_));
22+
methStack_.clear();
23+
typeStack_.resize(1);
24+
}
25+
26+
void MethodChainSetter::lazyPush(const char *begin, const char *end) const {
27+
methchain_ = std::shared_ptr<MethodChainBase>(new LazyMethodChain(lazyMethStack_));
28+
lazyMethStack_.clear();
29+
typeStack_.resize(1);
30+
}

0 commit comments

Comments
 (0)