I want to make a C++ object hierarchy of classifiers, which can be composed together via logical operators into a single classifier that implements the whole logical combination.This is actually for a particle physics analysis library, but I'll present a much simpler example which just classifies integers. The motivation is that a candidate physics object to be classified might have lots of floating point properties on which basis it could be kept or discarded: e.g. mass, energy, angles, etc. -- I can't possibly create all the possible combinations of function signatures to handle this, and even if I could, there's no way for the compiler to determine e.g. does the 3rd double mean mass or angle? So the intention is to make the users' code clearer and less ambiguous, while reducing the number of function signatures in my API and increasing flexibility.Here's an example of what I would like the resulting usage to look like, for the integer classifier example:Classifier c = IsOdd() || (IsEven() && LessThan(6));for (int i = 0; i < 10; ++i) {    if (c.classify(i)) cout << i <<  ;}// prints 0 1 2 3 4 5 7 9This demonstrates several requirements:I want to be able to do more than one logical combination in a single expressionThe objects are all stack-allocated -- no news are involved to mess up the syntax. new IsOdd() || (new IsEven() ... ) would both be distracting and would introduce problems of memory management for the user.The logical operators should have their usual meanings, precedences, and lazy evaluation behaviour: calling classify on a composite classifier should call the same function on each of the chain of logical combinations, stopping as soon as possible.The concrete classifiers (IsOdd, LessThan, etc.) should be objects with state, e.g. the constructor argument passed to LessThan rather than having to have a different LessThanX class for every integer!The base class should ideally be able to be instantiated rather than have to be handled through (const) references.I've made several attempts to implement this, but keep hitting problems. Typically these have been polymorphism-related: if each Classifier is to contain other classifier(s), then they need to be held as pointers (references can't be stored) and we're back to memory management issues. I found a way around this using the dynamic_any extension to boost's any:Infrastructure:#include <boost/any.hpp>#include <dynamic_any.hpp>using boost::any_cast;using boost::dynamic_any_cast;#include <string>#include <vector>#include <iostream>#include <cassert>#include <utility>using namespace std; // for clarity only, won't go in any public header!/// Main typesclass Classifier {public:  virtual bool classify(int a) const = 0;};class ClassifierAND : public Classifier {public:  bool classify(int a) const {    return dynamic_any_cast<const Classifier&>(classifiers.first).classify(a)      && dynamic_any_cast<const Classifier&>(classifiers.second).classify(a);  }  pair<boost::dynamic_any, boost::dynamic_any> classifiers;};class ClassifierOR : public Classifier {public:  bool classify(int a) const {    return dynamic_any_cast<const Classifier&>(classifiers.first).classify(a)      || dynamic_any_cast<const Classifier&>(classifiers.second).classify(a);  }  pair<boost::dynamic_any, boost::dynamic_any> classifiers;};/// Operator overloadstemplate <typename Classifier1, typename Classifier2>inline ClassifierAND operator && (const Classifier1& c1, const Classifier2& c2) {  ClassifierAND rtn;  rtn.classifiers.first = c1;  rtn.classifiers.second = c2;  return rtn;}template <typename Classifier1, typename Classifier2>inline ClassifierOR operator || (const Classifier1& c1, const Classifier2& c2) {  ClassifierOR rtn;  rtn.classifiers.first = c1;  rtn.classifiers.second = c2;  return rtn;}Concrete classifiers:struct IsEven : public Classifier {  bool classify(int a) const { return a % 2 == 0; }};struct LessThan : public Classifier {  LessThan(int val) { cutval = val; }  bool classify(int a) const { return a < this->cutval; }  int cutval;};struct GtrThan : public Classifier {  GtrThan(int val) { cutval = val; }  bool classify(int a) const { return a > this->cutval; }  int cutval;};Test program:void test(const Classifier& c) {  for (int i = -3; i < 10; ++i) {    if (c.classify(i)) cout << i <<  ;  }  cout << \n;}int main() {  IsEven e;  GtrThan g4(4);  GtrThan g2(2);  LessThan l(-1);  const Classifier& c1 = e || GtrThan(4);  test(c1);  // -2 0 2 4 5 6 7 8 9  ClassifierAND c2 = e && g2 && g4;  test(c2);  test(e && g2 && g4);  // 6 8 (twice)  test(e && l);  // -2  const Classifier& c4 = IsEven() || LessThan(3);  test(c4);  test(IsEven() || LessThan(3));  // -3 -2 -1 0 1 2 4 6 8 (twice)}As seen from the output, this does work... but I think/hope it can still be improved. In particular, the Classifier base class is virtual, which forces me to use const references: fine as an argument signature (cf. test) but a bit confusing for an explicit variable as in the last test and risks object slicing errors if used without care.Additionally the ClassifierAND/OR types are really clunky -- I'd originally tried to put a next Classifier member and a logical operation enum in the base class to avoid these pairs, but couldn't make it work nicely. It would be nice to hide these or avoid them entirely so that the compiler doesn't emit warnings about a chain of logically combined classifiers being of a type that the user isn't exposed to.I'm also not sure if the efficiency can be improved: due to the AND/OR pairs, there could in general be quite a lot of pair nesting.Any feedback and suggestions for improvements would be very welcome!
Implementing C++ boolean function objects with logical operator combinations
c++;object oriented;operator overloading