2010-05-09 36 views
1

我目前正在实现一个系统,其中包含许多表示客户端,业务,产品等对象的类。标准业务逻辑。正如人们所期望的,每个班级都有许多标准属性。当属性名称在运行时未知时,通过属性对C++对象进行通用聚合

我有基本相同的需求,如一个长长的清单:

  • 检索所有企业的,其产业制造能力。
  • 检索总部设在伦敦

类业务的所有客户端的能力,具有行业属性和客户端具有属性的位置。很明显,这是一个关系问题,在伪SQL中看起来像这样:

SELECT ALL business in business' WHERE sector == manufacturing 

不幸的是,插入数据库不是一个选项。

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

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

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

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

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

$ ./aggregations 5 

返回所有B的,其a对象 '我' 属性等于5请看下图:

#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())我想有一个单一的通用聚合fu它考虑了所有可能的类属性对,从而满足所有的聚合要求。并且在稍后添加附加属性或额外的聚合要求的情况下,将自动考虑这些要求。

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

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

+0

支持你能修改这些类吗? (如果你不行,你会遇到麻烦。) – Beta 2010-05-09 15:05:37

+0

@Beta:不一定,人们也可以想到为这些访问定义的函数对象。有很多可能性。 – 2010-05-09 17:56:50

+0

@Matthieu M .:我不明白。我可以只想两种方法来做到这一点,而无需为每个类的每个字段编写单独的例程:1)修改类或2)使用宏[shudder]。我很乐意学习另一种方法... – Beta 2010-05-09 18:51:25

回答

0

所以回答的主要问题,如何映射一个运行参数的类属性,是你不能

什么你要找的通常被称为运行时类型信息 (简称RTTI),即能够检索类型的信息运行时

C++不支持RTTI。无论是好还是坏,都不是,故事的结尾。

因此,你将不得不发明自己的东西。在广泛的笔画中,您将使所有B类实现一个接口,该接口将有一个类似于getClass(string className)的方法,该方法将返回指向您的A对象的指针,该对象又将实现另一个接口,该接口将具有类似于getAttribute(string attrName)的方法,然后您可以将其与您的价值进行比较。另一种可能性是A拥有方法compareToValue(string attrName, string value)而不是getAttribute,这样它就可以处理值的机制,而不仅仅是以字符串形式共享。

此外,您可以有相应的getAllClassesgetAllAttributes方法来检索列表 - 如果你需要的话。另外,如果你不绑定到C++,还有其他支持RTTI的平台。 Java和.NET将是两个例子。

1

您将不得不将数据存储在散列图或普通映射中,而不是原始C++数据。如果没有在C++的每个可能的值上进行切换/大小写/默认值,从字符串“海”到A.hai是不可能的。

至于前面的答案,这是反思,而不是RTTI。 C++确实有RTTI - 这就是typeid()的用途。但是,C++绝对不支持它,无论你想调用它。

1

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

你在找什么叫'反射',一个物体描述自己的能力。

反射是困难的,但它可以做到!

我通常是mettemplates和whatnot的粉丝,但这次我会提出一个纯粹的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的东西,但具有给定结构的名称的附加值。

恐怕它可以证明,虽然有点多OBSCUR,因为没有明显的收获......还有,我要补充的是,如果您有可能会愿意在语言直接代码,这本身:)

相关问题