views:

39

answers:

2

I'm creating a stat editor for some objects within a game world. Rather than have multiple edit menus for each object type, I just have one menu, and pass in a list/vector of stat-edit-objects which contain a pointer to the stat being edited, and the functions to do the work.

struct StatEditObjPureBase
{
    std::vector<std::string> reps;
    std::string name;
    std::basic_string<int> value;
    const iWindow* win;
    Editor* pE;

    StatEditObjPureBase(iWindow& rIW, Editor& rE) : win(&rIW), pE(&rE) {}

    virtual ~StatEditObjPureBase() = 0;
    virtual StatEditObjPureBase* clone() = 0;
    virtual void set() = 0;
    virtual void dec() = 0;
    virtual void inc() = 0;
    virtual void update() = 0;
};

struct StatEditObjWrap
{
    StatEditObjPureBase* base;

    StatEditObjWrap(StatEditObjPureBase* p) : base(p) {}
    StatEditObjWrap(const StatEditObjWrap& that) { base = that.base->clone(); }
    ~StatEditObjWrap() { delete base; }
};

template<class T> struct StatEditObj_INT : StatEditObjPureBase
{
    T* pMain;

    StatEditObj_INT(T* p, iWindow& rIW, Editor& rE) : StatEditObjPureBase(rIW, rE), pMain(p) {}
    StatEditObj_INT* clone() { return new StatEditObj_INT(*this); }
    void set();
    void dec(){--(*pMain);}
    void inc(){++(*pMain);}
    void update();
};

template<class T> void StatEditObj_INT<T>::set()
{
    *pMain = input_getInt(win->box_input_y, win->box_input_x, win->box_input_range, win->box_input_fg, win->box_input_bg);
}

template<class T> void StatEditObj_INT<T>::update()
{
    staticStatEditObj_intUpdate(*pMain, value, reps);
}

My main issue is having to indicate the type of the variable whose pointer is being stored in the template derived class. The following code is a small example, but you can assume that there will be hundreds of these stat edit object entries:

void Editor::statEditObjectTemplate(ObjectTemplate& r)
{
    std::vector<iWindowEntry> temp;
    iWindow iw(17, 60, temp);

    std::vector<StatEditObjWrap> stats;

    { StatEditObjWrap stat(new StatEditObj_INT<unsigned short>(&r.glyph, iw, *this)); stats.push_back(stat); }
    { StatEditObjWrap stat(new StatEditObj_INT<unsigned int>(&r.mappedFcolour, iw, *this)); stats.push_back(stat); }
    { StatEditObjWrap stat(new StatEditObj_INT<unsigned int>(&r.mappedBcolour, iw, *this)); stats.push_back(stat); }

    statEditor(stats, iw);
}

Is there a way to have the template typename for

new StatEditObj_INT<type>(&r.variable, iw, *this)

passed automatically?

(Note: type is for the first argument of StatEditObj_INT's constructor)

+2  A: 

Yes, you can use a factory function:

// Note:  Callee takes ownership of returned pointer
// (Alternatively, you should consider using a smart pointer like shared_ptr)
template <typename T>
StatEditObj_INT<T>* MakeNew_StatEditObj_INT(T* p, iWindow& rIW, Editor& rE)
{
    return new StatEditObj_INT<T>(p, rIW, rE);
}

Then you have:

stats.push_back(MakeNew_StatEditObj_INT(&r.glyph, iw, *this));
stats.push_back(MakeNew_StatEditObj_INT(&r.mappedFcolour, iw, *this));
stats.push_back(MakeNew_StatEditObj_INT(&r.mappedBcolour, iw, *this));

This works because when you use a function template, the compiler can deduce the template arguments from the function arguments (there are obviously cases where it can't, like if you have a template parameter that isn't represented in the function parameter list or is only represented in a non-deduced context). This is a common idiom when dealing with class templates.

James McNellis
In the example code OP gave, couldn't he just omit the type and let the compiler determine it (much how you're doing here with `MakeNew_StatEditObj_INT`?
JoshD
@JoshD: No, because you have to specify the type name in the new expression; the compiler can't deduce class template arguments from a constructor invocation.
James McNellis
@James McNellis: Thank you. I didn't know that.
JoshD
+1  A: 

Use a template member function in a non-template class.

struct StatEditObjWrap
{
  template <typename T>
  static StatEditObjWrap create(T* p, iWindow& rIW, Editor& rE);

  //...
}

template <typename T>
StatEditObjWrap StatEditObjWrap::create<T>(T* p, iWindow& rIW, Editor& rE) {
  return StatEditObjWrap( new StatEditObj_INT<T>( p, rIW, rE ) );
}

{
  vector<StatEditObjWrap> stats;
  stats.push_back(StatEditObjWrap::create(&r.glyph, iW, *this));
}
aschepler