Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C++ Object Introspection � (Enumerating Class Attributes)

0.00/5 (No votes)
14 Nov 2002 1  
Enumerating class attributes in C++.

Introduction

You've probably worked with programming languages that allowed you to inspect an object to determine what attributes make up the object at runtime (FoxPro, Perl etc). This feature is very handy if you need to write generic serialization routines. If you are programming in C++ then you probably also know that this type of introspection is missing.

Using the code

I've tried to create a series of macros and classes which will allow you to expose the attributes of your classes at runtime, with little change to your existing code. To accomplish this, all you need to do is inherit your class from CExposeAttributes and in the constructor of your class add one line for every attribute. Of course, I am making a big assumption. The assumption is that you have implemented Get/Set methods for all of your attributes. CExposeAttributes works by maintaining a list of function pointers to those Get/Set methods. This exposes the class' attributes, but only through the public methods. In addition, I have only implemented support for the following data types: long, double, CString, CGuid, bool and LPCTSTR. The Get/Set methods must conform to the following function signatures:

//

    long:
        YourSetFunction(long l);
        long YourGetFunction() const;
    double:
        YourSetFunction(double l);
        double YourGetFunction() const;
    bool:
        YourSetFunction(bool l);
        bool YourGetFunction() const;
    LPCTSTR:
        YourSetFunction(LPCTSTR l);
        LPCTSTR YourGetFunction() const;
    CString:
        YourSetFunction(const CString& l);
        CString YourGetFunction() const;
    CGuid:
        YourSetFunction(const CGuid& l);
        CGuid YourGetFunction() const;

If you adhere to some other standard of Get/Set methods, you will need to modify or add support for the function pointer typedef's in CGetAttribute and CSetAttribute. Instructions are in the code (serialize.h) for adding support for additional data types.

//

//SAMPLE Class



class CSampleAttribute : public CXMLSerialization,
                 CExposeAttributes<csampleattribute>
{
public:
    CSampleAttribute();
    virtual ~CSampleAttribute();

    void SetName(LPCTSTR sName){m_sName=sName;};
    LPCTSTR GetName() const {return m_sName;};

    void SetSize(double dSize){m_dSize=dSize;};
    double GetSize() const {return m_dSize;};

    void SetLength(long lLength){m_lLength=lLength;};
    long GetLength() const {return m_lLength;};

    void SetID(const CGuid& oID){m_ID=oID;};
    CGuid GetID() const {return m_ID;};

    bool Save(MSXML2::IXMLDOMElementPtr& pParentElement);
    bool Load(MSXML2::IXMLDOMElementPtr& pElement,const CGuid& oID);

private:
    CString m_sName;
    double m_dSize;
    long m_lLength;
    CGuid m_ID;
};
</csampleattribute>

Assuming you have your Get/Set methods defined, you are ready to declare the attributes. To do this, in your class' constructor use one of the following macros:

//

CSampleAttribute::CSampleAttribute()
{
    //Declare all of the attributes by identifying 

    //a name and the corresponding Set/Get methods

    SET_CGUID_ATTRIBUTE(CSampleAttribute,"ID",SetID,GetID);
    SET_LPCTSTR_ATTRIBUTE(CSampleAttribute,"Name",SetName,GetName);
    SET_LONG_ATTRIBUTE(CSampleAttribute,"Length",SetLength,GetLength);
    SET_DOUBLE_ATTRIBUTE(CSampleAttribute,"Size",SetSize,GetSize);
}

That's it! Your class now has a couple of methods that will allow you to enumerate all of it's attributes. Calling GetGetAttributes or GetSetAttributes will return you a list of CGetAttribute or CSetAttribute. You can write serialization classes that can enumerate through any object and read/write its attributes. I have included a sample class called CXMLSerialization. If you inherit your class from CXMLSerialization then you will inherit two methods:

virtual bool Save(MSXML2::IXMLDOMElementPtr& pParentElement) =0;
virtual bool Load(MSXML2::IXMLDOMElementPtr& pElement, const CGuid& oID) =0;

Override these methods to allow your class to write its attributes to an XML element.

//

bool CSampleAttribute::Save(MSXML2::IXMLDOMElementPtr& pParentElement)
{
    //Override the save method of CXMLSerialization

    ASSERT(pParentElement!=NULL);

    //Don't save if we don't need to

    if (GetDirty()== false)
        return true;

    CString sXPATH;
    CXMLAttribute<csampleattribute> oAttribute;

    //Find the correct element (if it exists)

    sXPATH.Format("%s[@%s='%s']","SampleAttribute", 
                          "ID",GetID().CStrValUCase());
    MSXML2::IXMLDOMElementPtr pCur = 
        pParentElement->selectSingleNode((LPCTSTR)sXPATH);
    if (pCur == NULL)
    {
      //It didn't exists, create the element

      pCur = 
       pParentElement->GetownerDocument()->createElement("SampleAttribute");
      pParentElement->appendChild(pCur);
    }

    oAttribute.Save(pCur,this,this);
    SetDirty(false);
    return true;
}
bool CSampleAttribute::Load
   (MSXML2::IXMLDOMElementPtr& pElement,const CGuid& oID)
{
    ASSERT(pElement!=NULL);
    CXMLAttribute<csampleattribute> oAttribute;

    oAttribute.Load(pElement,this,this);
    SetDirty(false);
    return true;
}
</csampleattribute></csampleattribute>

CXMLAttribute knows how to load and save all types of attributes to and from XML. A similar class could easily be created to read/write to a relational database.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here