我想要使C++对象层次结构的“分类器”,它可以一起通过逻辑运算符组成一个单一的分类器,实现整个逻辑组合。
这实际上是一个粒子物理分析库,但我将给出一个简单得多的例子,它只对整数进行分类。其动机是,待分类的候选物理对象可能具有许多浮点特性,可以保留或丢弃它。例如质量、能量、角度等--我不可能创建所有可能的函数签名组合来处理这个问题,即使我可以,编译器也无法确定例如“第三个双平均质量或角度吗?”因此,这样做的目的是使用户的代码更清晰、更少模棱两可,同时减少我的API中的函数签名数量,并增加灵活性。
对于整数分类器示例,下面是我希望得到的结果用法的示例:
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 9这说明了几项要求:
news来搞乱语法。new IsOdd() || (new IsEven() ... )既会分散注意力,又会给用户带来内存管理问题。classify应该在每个逻辑组合链上调用相同的函数,并尽快停止。IsOdd、LessThan等)应该是状态对象,例如传递给LessThan的构造函数参数,而不必对每个整数都有不同的LessThanX类!我已经做了几次尝试来实现这一点,但是不断地遇到问题。通常这些都与多态相关:如果每个Classifier都包含其他分类器(S),那么它们需要作为指针保存(引用不能存储),我们回到内存管理问题上。我找到了一种利用dynamic_any扩展来增强其任何的方法:
#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 types
class 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 overloads
template <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;
}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;
};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)
}从输出上看,这确实有效..。但我认为/希望它还能得到改进。特别是,Classifier基类是虚拟的,这迫使我使用const引用:罚款作为参数签名(cf )。( test),但对于显式变量(如上一次测试)来说有点混乱,如果不小心使用,可能会出现“对象切片”错误。
此外,ClassifierAND/OR类型真的很笨重--我最初尝试在基类中放置一个"next“Classifier成员和一个逻辑操作枚举,以避免这些对,但无法使它很好地工作。最好将它们隐藏起来,或者完全避免它们,这样编译器就不会发出警告,说明一个逻辑组合的分类器链是用户不公开的类型。
我也不确定是否可以提高效率:由于和/或对,通常会有相当多的对嵌套。
任何反馈意见和改进建议将是非常欢迎的!
发布于 2012-10-24 21:14:28
我认为使用模板和鸭类型将获得更好的成功,而不是多态界面。C++标准库使用鸭子类型来实现您在这里试图实现的相同影响。
这就是我要做的:
// A couple of these are already in the standard library have a look.
struct IsEven
{
bool operator()(int a) const { return a % 2 == 0; }
};
struct LessThan
{
LessThan(int val): cutval(val) {}
bool operator()(int a) const { return a < cutval; }
int cutval;
};
struct GtrThan
{
GtrThan(int val): cutval(val) {}
bool operator()(int a) const { return a > this->cutval; }
int cutval;
};
template<typename T1, typename T2>
struct AndOp
{
AndOp(T1 const& t1, T2 const& t2) : t1(t1), t2(t2) {}
T1 const t1;
T2 const t2;
bool operator()(int a) const { return t1(a) && t2(a);}
};
template<typename T1, typename T2>
struct OrOp
{
OrOp(T1 const& t1, T2 const& t2) : t1(t1), t2(t2) {}
T1 const t1;
T2 const t2;
bool operator()(int a) const { return t1(a) || t2(a);}
};
template<typename T1, typename T2>
AndOp<T1, T2> make_and_op(T1 const& t1, T2 const& t2)
{
return AndOp<T1, T2>(t1, t2);
}
template<typename T1, typename T2, typename T3>
AndOp<AndOp<T1, T2>, T3> make_and_op(T1 const& t1, T2 const& t2, T3 const& t3)
{
return AndOp<AndOp<T1, T2>, T3>(make_and_op(t1, t2), t3));
}
template<typename T1, typename T2>
OrOp<T1, T2> make_or_op(T1 const& t1, T2 const& t2)
{
return OrOp<T1, T2>(t1, t2);
}现在我们可以用以下方法进行测试:
template<typename T>
void test(const T& c) {
for (int i = -3; i < 10; ++i) {
if (c(i)) cout << i << " ";
}
cout << "\n";
}
int main()
{
test(make_or_op(IsEven(), GtrThan(4)));
// -2 0 2 4 5 6 7 8 9
test(make_and_op(IsEven(), GtrThan(2), GtrThan(4)));
// 6 8
test(make_and_op(IsEven(), LessThan(-1)));
// -2
test(make_or_op(IsEven(), LessThan(3)));
// -3 -2 -1 0 1 2 4 6 8
}https://codereview.stackexchange.com/questions/17881
复制相似问题