1

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

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

  • 检索所有行业为制造业的业务的能力。
  • 检索所有位于伦敦的客户的能力

类业务有属性部门,客户有属性位置。显然这是一个关系问题,在伪 SQL 中看起来像:

SELECT ALL business in business' WHERE sector == manufacturing

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

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

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

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

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

由于不发布代码是不礼貌的,下面是一个虚拟程序,它创建了一堆富有想象力的类的对象。包括一个特定的聚合函数,它返回 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() ),我希望有一个通用聚合函数,它考虑所有可能的类属性对,以便满足所有聚合要求.. 并且在事件中附加属性稍后添加,或额外的聚合要求,这些将自动被考虑在内。

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

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

4

3 回答 3

1

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

您正在寻找的东西称为“反射”,即对象描述自身的能力。

反思是困难的,但它仍然可以做到!

我通常是元模板之类的粉丝,但这次我将提出一个纯粹的 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但具有给定结构的名称的附加值。

恐怕它可能会变得更加晦涩难懂,但没有明显的收获……此外,我应该补充一点,如果可能的话,您可能愿意直接使用本机支持此功能的语言进行编码:)

于 2010-05-09T17:46:59.383 回答
1

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

至于前面的回答,这是反射,不是RTTI。C++ 确实有 RTTI - 这就是 typeid() 的用途。但是,C++ 绝对不支持它,不管你想怎么称呼它。

于 2010-05-09T15:58:38.330 回答
0

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

您要查找的通常称为运行时类型信息(简称 RTTI),即在运行时检索类型信息的能力。

C++ 不支持 RTTI。不管是好是坏,它只是没有,故事的结尾。

因此,您将不得不发明自己的东西。概括地说,您将使所有B类实现一个接口,该接口将具有类似的方法getClass( string className ),该方法将返回指向您的A对象的指针,而该对象又将实现另一个具有类似方法的接口,getAttribute( string attrName )然后您将能够将其与您的价值进行比较。另一种可能性是A使用 methodcompareToValue( string attrName, string value )而不是getAttribute,以便它可以处理值的机制,而不仅仅是作为字符串出现。

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

此外,如果您未绑定到 C++,还有其他支持 RTTI 的平台。Java 和 .NET 就是两个例子。

于 2010-05-09T15:09:47.547 回答