当属性名称在运行时未知时,按属性的C ++对象的通用聚合

时间:2010-05-09 14:47:13

标签: c++ class mapping attributes aggregation

我目前正在实现一个系统,其中包含许多类,表示客户端,业务,产品等对象。标准业务逻辑。正如人们可能期望每个类都有许多标准属性。

我有一长串基本相同的要求,例如:

  • 能够检索行业正在制造的所有业务。
  • 能够检索所有位于伦敦的客户

类业务具有属性扇区,客户端具有属性位置。很明显,这个关系问题和伪SQL看起来像是:

SELECT ALL business in business' WHERE sector == manufacturing

不幸的是,插入数据库不是一种选择。

我想要做的是拥有一个通用聚合函数,其签名采用以下形式:

vector<generic> genericAggregation(class, attribute, value);

其中class是我想要聚合的对象类,属性和值是类属性和感兴趣的值。在我的例子中,我将vector作为返回类型,但这不起作用。可能更好地声明相关类类型的向量并将其作为参数传递。但这不是主要问题。

如何以字符串形式接受类,属性和值的参数,然后将它们映射到通用对象聚合函数中?

由于不发布代码是不礼貌的,下面是一个虚拟程序,它创建了一堆富有想象力的类对象。包括特定的聚合函数,该函数返回B对象的向量,其A对象等于在命令行指定的id,例如..

$ ./aggregations 5

返回A对象'i'属性等于5的所有B。见下文:

#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>

using namespace std;

//First imaginativly names dummy class
class A {
 private:
  int i;
  double d;
  string s; 
 public:
  A(){}
  A(int i, double d, string s) {
   this->i = i;
   this->d = d;
   this->s = s;
  }
  ~A(){}
  int getInt() {return i;} 
  double getDouble() {return d;} 
  string getString() {return s;}
};

//second imaginativly named dummy class
class B {
 private:
  int i;
  double d;
  string s;
  A *a; 
 public:
  B(int i, double d, string s, A *a) {
   this->i = i;
   this->d = d;
   this->s = s;
   this->a = a;
  }
  ~B(){}
  int getInt() {return i;} 
  double getDouble() {return d;} 
  string getString() {return s;}
  A* getA() {return a;}
};

//Containers for dummy class objects
vector<A> a_vec (10);
vector<B> b_vec;//100

//Util function, not important..
string int2string(int number) {
 stringstream ss;
 ss << number;
 return ss.str();
}


//Example function that returns a new vector containing on B objects
//whose A object i attribute is equal to 'id'
vector<B> getBbyA(int id) {
 vector<B> result;
 for(int i = 0; i < b_vec.size(); i++) {
  if(b_vec.at(i).getA()->getInt() == id) {
   result.push_back(b_vec.at(i));
  }
 }
 return result;
}



int main(int argc, char** argv) {

 //Create some A's and B's, each B has an A...
 //Each of the 10 A's are associated with 10 B's.
 for(int i = 0; i < 10; ++i) {
  A a(i, (double)i, int2string(i));
  a_vec.at(i) = a;
  for(int j = 0; j < 10; j++) {
   B b((i * 10) + j, (double)j, int2string(i), &a_vec.at(i));   
   b_vec.push_back(b);
  }
 }

 //Got some objects so lets do some aggregation

 //Call example aggregation function to return all B objects 
 //whose A object has i attribute equal to argv[1]
 vector<B> result = getBbyA(atoi(argv[1]));

 //If some B's were found print them, else don't...
 if(result.size() != 0) {
  for(int i = 0; i < result.size(); i++) {
   cout << result.at(i).getInt() << " " << result.at(i).getA()->getInt() << endl;
  }
 }
 else {
  cout << "No B's had A's with attribute i equal to " << argv[1] << endl;
 }

 return 0;
}

编译:

g++ -o aggregations aggregations.cpp

如果您愿意:)

我没有实现一个单独的聚合函数(即示例中的getBbyA()),而是希望有一个通用聚合函数来解释所有可能的类属性对,以满足所有聚合要求。事件附加属性稍后添加,或其他聚合要求,这些将自动计算。

所以这里有一些问题,但我正在寻求深入了解的主要问题是如何将运行时参数映射到类属性。

我希望我已经提供了足够的细节来充分描述我正在做的事情......

3 个答案:

答案 0 :(得分:1)

您必须将数据存储在散列映射或法线贴图中,而不是原始C ++数据中。如果不对C ++中的每个可能值执行switch / case / default,就不可能从字符串“hai”转到A.hai。

至于上一个答案,这是反思,而不是RTTI。 C ++确实有RTTI - 这就是typeid()的用途。但是,C ++肯定不支持它,无论你想称它为什么。

答案 1 :(得分:1)

你肯定提供了足够的细节:)我希望你明白它并不容易。

您正在寻找的是“反射”,一种物体描述自身的能力。

反思很困难,但仍然可以做到!

我通常是metatemplates和诸如此类的粉丝,但这一次我将提出一个纯粹的OO-Way。从某种意义上说,你必须实际修改你的对象以支持它。

基本思想是以支持这种习语的语言本身的方式来做它。

1-我们调整基本类型

enum Type
{
  Int,
  String
};

class Field
{
public:
  Type type() const { return mType; }

  virtual Field* clone() const = 0;

  virtual std::string toString() const = 0;
  virtual void fromString(const std::string& s) = 0;

  virtual ~Field() {}
protected:
  explicit Field(Type t): mType(t) {}
private:
  Type mType;
};

bool operator==(const Field& lhs, const Field& rhs)
{
  return lhs.type() == rhs.type() && lhs.toString() == rhs.toString();
}

class IntField: public Field
{
public:
  typedef int data_type;

  IntField(): Field(Int), mData() {}

  virtual IntField* clone() const { return new IntField(*this); }

  data_type get() const { return mData; }
  void set(data_type d) { mData = d; }

  virtual std::string toString() const
  { return boost::lexical_cast<std::string>(mData); }

  virtual void fromString(const std::string& s)
  { mData = boost::lexical_cast<data_type>(s); }

private:
  data_type mData;
};

// Type information allow for more efficient information
bool operator==(const IntField& lhs, const IntField& rhs)
{
  return lhs.get() == rhs.get();
}

2-然后我们建立了一个能够容纳所有这些

的类
class Object
{
public:
  virtual ~Object(); // deal with memory

  Field* accessAttribute(const std::string& name);
  const Field* getAttribute(const std::string& name) const;
  void setAttribute(const std::string& name, const Field& value);

protected:
  // Deal with memory
  Object();
  Object(const Object& rhs);
  Object& operator=(const Object& rhs);

  void addAttribute(const std::string& name, const Field& value);

private:
  std::map<std::string, Field*> mFields; // tricky, we must deal with memory here
};

3-用法:

class Person: public Object
{
public:
  Person(const std::string& surname,
         const std::string& givenname,
         int age): Object()
  {
    this->addAttribute("surname", StringField(surname));
    this->addAttribute("givenname", StringField(givenname));
    this->addAttribute("age", IntField(age));
  }
};

int main(int argc, char* argv[])
{
  // initialize
  std::vector<Person> persons = /**/;

  std::cout << "Please enter an attribute and the expected value" << std::endl;
  std::string name, value;
  std::cin >> name >> value;

  std::vector<Person*> result;
  for (std::vector<Person>::iterator it = persons.begin(), end = persons.end();
       it != end; ++it)
  {
    const Field* field = it->getAttribute(name);
    if (field && field->toString() == value) result.push_back(&(*it));
  }

  std::cout << "Selected persons for " << name
            << " = " << value << " are:\n";
  for (std::vector<Person*>::iterator it = result.begin(), end = result.end();
       it != end; ++it)
  {
    const Person& person = **it;
    std::cout << "  " << person.surname() << " " << person.givenname() << "\n";
  }
  std::cout.flush();
}

可能有许多其他方式,或多或少自动化。值得注意的是,人们可以想到现有类的适应结构。与宏BOOST_FUSION_ADAPT_STRUCT类似的东西,但具有给定结构名称的附加值。

我担心它可能会更加严重,因为没有明显的收获......而且,我应该补充一点,如果可能的话,你可能愿意直接用支持这种语言的语言编写代码:)

答案 2 :(得分:0)

所以主要问题如何将运行时参数映射到类属性的答案是你不能

您正在寻找的内容通常称为运行时类型信息(简称RTTI),即能够在运行时检索类型的信息< / em>的

C ++不支持RTTI。无论好坏,它只是没有,故事结束。

因此,你必须发明自己的东西。粗略地说,您将使所有B类实现一个接口,该接口将具有getClass( string className )之类的方法,该方法将返回指向您的A对象的指针,而该指针又会实现另一个具有getAttribute( string attrName )方法的接口,然后您就可以将它与您的值进行比较。另一种可能性是A使用方法compareToValue( string attrName, string value )而不是getAttribute,这样它就可以处理值的机制,而不仅仅是作为字符串进行同步。

此外,如果需要,您可以使用相应的getAllClassesgetAllAttributes方法来检索列表。

此外,如果您没有使用C ++,还有其他平台支持RTTI。 Java和.NET将是两个例子。