Home > Mobile >  the best design for mapping enums to const classes
the best design for mapping enums to const classes

Time:12-22

I have this example that works : I create an object that implements "PartInterface" for each value of the enum and add them in a map. But I don't find this satisfactory, because everything could be inferred at compile time rather than at runtime. Is there a more elegant way to do this in c 11? Another solution would be to build the "YearPart", "MonthPart" and "DayPart" objects at each call of the "get" function, but it seems to me less efficient...

#include <iostream>

#include <map>
#include <memory>

struct Date{
    int year;
    int month;
    int day;
};

class PartInterface{
public:
    virtual const std::string & getName()const=0;
    virtual int getValue(const Date& d)const=0;
    virtual ~PartInterface(){}
};

class Part : public PartInterface{
public:
    Part(const std::string& name):_name(name){}

    const std::string & getName()const{
        return _name;
    }
    virtual int getValue(const Date& d)const=0;
    
private:
    std::string _name;
};

class YearPart : public Part{
public:
    YearPart():Part("year"){}
    int getValue(const Date& d)const{
        return d.year;
    }
};

class MonthPart : public Part{
public:
    MonthPart():Part("month"){}
    int getValue(const Date& d)const{
        return d.month;
    }
};

class DayPart : public Part{
public:
    DayPart():Part("day"){}
    int getValue(const Date& d)const{
        return d.day;
    }
};

enum DatePart{
    Year,
    Month,
    Day
};

class Parts{
public:
    Parts(){
        _map[Year].reset(new YearPart());
        _map[Month].reset(new MonthPart());
        _map[Day].reset(new DayPart());
    };
    
    const PartInterface& get(const DatePart& date_part)const{
        return * (_map.find(date_part)->second);
    }
    
private:
    std::map<DatePart, std::unique_ptr<PartInterface> > _map;
};

int main() {
  Date d({2016, 7, 23});
  const Parts parts;
  std::cout << "Date " 
    << parts.get(Year).getValue(d) << " " 
    << parts.get(Month).getValue(d) << " " 
    << parts.get(Day).getValue(d) << std::endl;
  return 0;
}

CodePudding user response:

To be honest I don't understand why you need this whole machinery when a simple int getPart(DatePart,Date) would suffice. Most of the inefficiencies in the code seem to be selfmade. Anyhow, to adress only the mapping from enum to something else at compile time, you can use a template and specialize it for the enum values (I left out all the other stuff, because I dont understand it, but you can add it back, the solution applies the same):

#include <iostream>

struct Date{
    int year;
    int month;
    int day;
};

enum DatePart{
    Year,
    Month,
    Day
};

template <DatePart DP>
int getPart(const Date&);

template <> int getPart<Year>(const Date& d){ return d.year;}
template <> int getPart<Month>(const Date& d){ return d.month;}
template <> int getPart<Day>(const Date& d){ return d.day;}

int main() {
  Date d({2016, 7, 23});
  std::cout << "Date " 
    << getPart<Year>(d) << " " 
    << getPart<Month>(d) << " " 
    << getPart<Day>(d) << std::endl;
}

Live Demo

As mentioned above, if you really need the Part stuff, the solution applies as well. Instead of specializing a template to return different members based on the enum value you can specialize it to return different derived instances of some base class (it could even be different return types, you don't need runtime polymorphism when you want to choose the "part" at compile time anyhow).

  • Related