Flat Member Traversions (flatforeach.h)

A user defined function may be invoked on each member variable of a reflected class. Member variables that are iterable (either being std::container or reflected classes) are not treated differently, pointers are not dereferenced inside the traversion. In particular flat traversions do not consider base classes of member variables.
These traversions come in two flavours (where the second can only reach static member variables):

template<class ReflectedClass, class Function, class MemberKinds> 
void foreachAttribute(ReflectedClass& dataTuple,Function&function,MemberKinds);   

template<class ReflectedClass, class Function> 
void foreachAttribute(Function& function);

To combine this with base class traversion you can use these functions:

template<class ReflectedClass, class Function, class MemberKinds> 
void foreachAttributeInclBaseClasses(ReflectedClass& dataTuple,Function&function,MemberKinds);   

template<class ReflectedClass, class Function> 
void foreachAttributeInclBaseClasses(Function& function);

When the traversion starts with an instance of a reflected class the user may choose to iterate over static members only, over non-static members only or over static and non static members. According to that the type MemberKinds can be any of the following:

A user defined attribute function object that can be used to iterate over members of an instance of a reflected class must have overloaded function call operators that can be called with a reference to each member variable, the nametag of the corresponding member variable and a pointer to the enclosing class. These operators can be declared as a member template function like this:

struct UserDefinedAllForeachVisitor
{
    template<class ValueType, class NameTag, class Scope> 
    void operator()(ValueType& attribute, NameTag, Scope*);
};

An example for such an attribute function object is the RecordInitializer (initreflectedmembers.h). A user defined function that can be used with the second form must have overloaded template function call operators that can be called with the type of the enclosing class as template parameter and a reference to each member variable, and the nametag of the corresponding member variable. These operators can be declared as a member template function like this:

struct UserDefinedAllForeachStaticMemberVisitor
{
    template<class Scope, class ValueType, class NameTag> 
    void operator()(ValueType& attribute, NameTag);
};

The astute reader might have observed that you can kill two birds with a stone here:

struct UserDefinedStaticAndInstanceAllForeachVisitor
{
    template<class ValueType, class NameTag, class Scope> 
    void operator()(ValueType& attribute, NameTag, Scope* = NULL);
};

For Visual C++ 6.0 the signature for the form that starts iteration without an instance of a reflected class has to be like this:

    template<class Scope, class ValueType, class NameTag> 
    void operator()(ValueType& attribute, NameTag, refl::PassType<Scope>);
    

and if you are interested in the two birds you can make it like that:

    template<class WrappedScope, class ValueType, class NameTag> 
    void operator()(ValueType& attribute, NameTag, WrappedScope);
    

If the traversion did start with an instance of a reflected class then WrappedScope will be an instance of PassScope<ReflectedClass> and this template has a nested typedef on the ReflectedClass named type and has a membervariable named scope_ that is a pointer to an instance of the ReflectedClass.
Otherwise WrappedScope will be instance of PassType<ReflectedClass> and this template has a nested typedef on the ReflectedClass named type and no member variable.

Example

Gratifications, incentives and bonuses can come in a variety of forms. Assume that all these can be stored as member variables (and for the sake of simplicity all these are plain strings).

A traversion that accumulates all values that are incentives has to be able to recognize incentives.
This can be done at compiletime using the following mapping:

struct Applies{};

struct DoesNotApply{};
template<class Tag> struct PartOfIncentiveProgram
{
    typedef DoesNotApply type;
};
#define INCENTIVE_PART(Tag)                     \
template<> struct PartOfIncentiveProgram<Tag>   \
{                                               \
    typedef Applies type;                       \
};
    

Some personnel to populate the example:  

class Employee
{
    BEGIN_REFLECTION(Employee)
    DEF_REFLECTED_ATTRIBUTE(std::string, BusyBee)
    DEF_REFLECTED_ATTRIBUTE(short, Salary)          // it's a hard world out there :-)
    DEF_REFLECTED_ATTRIBUTE(std::string,Sellerie)   // healthy food can compensate a lot
    // other attributes omitted :-)
    END_REFLECTION
    virtual ~Employee(){}
};

INCENTIVE_PART(Employee::BusyBee)

class Boss : public virtual Employee
{ 
    BEGIN_REFLECTION(Boss)
    DEF_REFLECTED_VIRTUAL_BASECLASS(Employee)    
    DEF_REFLECTED_ATTRIBUTE(std::string, ReducedWages)
    DEF_REFLECTED_ATTRIBUTE(std::string, IncreasedProduktivity)
    DEF_REFLECTED_STATIC_ATTRIBUTE(std::string, ParkingLot)
    // each boss has a parking lot :-)
    DEF_REFLECTED_ATTRIBUTE(std::vector<Employee>, Staff)
    END_REFLECTION
};
INCENTIVE_PART(Boss::ReducedWages)
INCENTIVE_PART(Boss::IncreasedProduktivity)
INCENTIVE_PART(Boss::ParkingLot)
// the staff does not contribute to the boss'es incentives

class ChiefTechnicalOfficer : public Boss
{ 
    BEGIN_REFLECTION(ChiefTechnicalOfficer)
    DEF_REFLECTED_VIRTUAL_BASECLASS(Boss)    
    DEF_REFLECTED_ATTRIBUTE(std::string, ReducedBugs)
    DEF_REFLECTED_STATIC_ATTRIBUTE(std::string, FreeSoftwareBundle) 
    END_REFLECTION
};
INCENTIVE_PART(ChiefTechnicalOfficer::ReducedBugs)
INCENTIVE_PART(ChiefTechnicalOfficer::FreeSoftwareBundle)

The attribute function used to accumulate the incentives:

class IncentiveAccumulator
{
public:
    template<class Scope, class ValueType, class NameTag> 
    void operator()(ValueType& attribute, NameTag nameTag, Scope* = NULL)
    {
        typedef typename PartOfIncentiveProgram<NameTag>::type CouldBePartOfIncentives;
        accumulateValue<Scope>(attribute, nameTag, CouldBePartOfIncentives());
    }
    std::string result()const
    {
        return incentives_.str();
    }
private:
    template<class Scope, class Value, class NameTag> void 
    accumulateValue(Value& incentive,NameTag nameTag, Applies)
    {
        incentives_ << Scope::getClassName() << "." << Scope::getAttributeName(nameTag)<< ":";
        incentives_ << incentive << "\t";
    }
    template<class Scope, class Value, class NameTag> void 
    accumulateValue(Value&,NameTag, DoesNotApply)
    { }
    std::stringstream incentives_;
};

(for Visual C++ 6.0 a workaround can be used).

 

For the sake of elegance:

template<class AnyOne> std::string accumulateIncentives(const AnyOne& annie)
{
    IncentiveAccumulator collect;
    refl::foreachAttributeInclBaseClasses(annie,collect,refl::TraverseInstanceAndClassVariables());
    return collect.result();
}
template<class AnyOne> std::string accumulateIncentives()
{
    IncentiveAccumulator collect;
    // to accumulate only the incentives that pertain to the position annie holds:
    refl::foreachAttributeInclBaseClasses<AnyOne>(collect);
    return collect.result();
}

test data

std::string Boss::ParkingLot_ = "a lot";
std::string ChiefTechnicalOfficer::FreeSoftwareBundle_ = "Office IP";
. . .    
ChiefTechnicalOfficer annie;
annie.setReducedWages("by 10%");
annie.setIncreasedProduktivity("by - 20%");
annie.setBusyBee("from 10 to 10");
std::cout << accumulateIncentives(annie) << '\n';
std::cout << accumulateIncentives<ChiefTechnicalOfficer>() << '\n';
    

output

Employee.BusyBee:from 10 to 10 Boss.ReducedWages:by 10% Boss.IncreasedProduktivity:by - 20% Boss.ParkingLot:a lot ChiefTechnicalOfficer.ReducedBugs: ChiefTechnicalOfficer.FreeSoftwareBundle:Office IP

Boss.ParkingLot:a lot ChiefTechnicalOfficer.FreeSoftwareBundle:Office IP

Last revised: September 13, 2004 Copyright © 2004 Arne Adams