Citat:
tosa:
Overhead postoji zato sto to ne mogu da budu inline f-je !
Kao sto sam napisao, to mi treba za COM objekat a ne za internu upotrebu u
nekom programu ...
tosa
U poslednjem CUJ (Online) postoji tekst o State Design Patternu
pomocu pokazivaca na funkcije clanice koje takodje ima
call overhead, ali me je nateralo da probam jos jednom
(cisto da se ostane u formi ;)
Evo resenja (prilicno lici na ono iz CUJ ali ima svojih prednosti):
Code:
#include <iostream>
#include <cassert>
template<typename ReturnTypeT, class ClassT, typename SignatureT>
struct FunctionTraits {
typedef ReturnTypeT ReturnType;
typedef ClassT ClassType;
typedef SignatureT Signature;
};
template <class StateType, class Cfg1Type, class Cfg2Type>
class Selector {
StateType state;
public:
Selector (StateType s) : state (s) {}
void Change (StateType s) { state = s; }
template <typename FuncTraits>
typename FuncTraits::Signature Select (const FuncTraits &, typename FuncTraits::ClassType &c) const {
FuncTraits method_switcher;
typename FuncTraits::Signature mem_func = 0;
switch (state) {
case Cfg1Type::choose:
mem_func = Cfg1Type::get_func(method_switcher);
break;
case Cfg2Type::choose:
mem_func = Cfg2Type::get_func(method_switcher);
break;
default:
assert (0);
}
return mem_func;
}
};
class Cls {
public:
struct Dispatcher {
typedef FunctionTraits<void, Cls, void (Cls::*)()> Func1;
typedef FunctionTraits<bool, Cls, bool (Cls::*)(int)> Func2;
};
private:
typedef int StateType;
struct CfgA {
enum { choose = 1 };
static Dispatcher::Func1::Signature get_func(Dispatcher::Func1 &) { return &Cls::f1a; }
static Dispatcher::Func2::Signature get_func(Dispatcher::Func2 &) { return &Cls::f2a; }
};
struct CfgB {
enum { choose = 2 };
static Dispatcher::Func1::Signature get_func(Dispatcher::Func1 &) { return &Cls::f1b; }
static Dispatcher::Func2::Signature get_func(Dispatcher::Func2 &) { return &Cls::f2b; }
};
void f1a () { std::cout << "f1a" << std::endl; }
void f1b () { std::cout << "f1b" << std::endl; }
bool f2a (int) { std::cout << "f2a" << std::endl; return true; }
bool f2b (int) { std::cout << "f2b" << std::endl; return true; }
friend struct Cls::CfgA;
friend struct Cls::CfgB;
Selector<StateType, CfgA, CfgB> selector;
public:
enum { STATE_A = CfgA::choose, STATE_B = CfgB::choose };
Cls () : selector (STATE_A) {}
void Change (StateType state) { selector.Change (state); }
void f1 ();
bool f2 (int n);
};
void Cls::f1 () { (this->*(selector.Select(Dispatcher::Func1(), *this)))(); }
bool Cls::f2 (int n) { return (this->*(selector.Select(Dispatcher::Func2(), *this)))(n); }
int main ()
{
Cls cl;
cl.f1 ();
cl.f2 (1);
cl.Change (Cls::STATE_B);
cl.f1 ();
cl.f2 (1);
cl.Change (Cls::STATE_A);
cl.f1 ();
cl.f2 (1);
return 0;
}
Gde su prednosti:
1. Pozivi metoda klase nisu inline, pa moze da se koristi za COM objekte.
Naravno, ako ne postoji takvo ogranicenje, mogu biti i inline.
2. Sve sto se odigra u pozvanoj funkciji jeste inline.
3. Nema poziva virtuelnih funkcija.
4. Mozes da napravis vise grupa metoda i da ih posebno konfigurises -
trebaju ti samo CfgX klase i odgovarajuci selector objekat.
Ako ti treba varijanta sa vise konfiguracija od dve, promeni Selector.
I on bi mogao da se napravi u varijanti za 2, 3, 4... konfiguracije.
Neke stvari su malo ruzne (npr. izbor Select funkcije pomocu dummy parametra umesto pomocu
Select<Dispatcher::Func1>(*this), i Dispatcher je public zato sto VC++ tu pogresno
(bar mislim) prijavljuje greske. GCC se mnogo vise ponasa onako kako bi covek to ocekivao
od lepo vaspitanog kompajlera iz dobre kuce.
Ostaje problem sto se pri prekonfigurisanju promeni samo mapiranje metoda, a promenljive clanice
ostaju iste, ali mislim da bi i to moglo da se napravi. Npr. u Selector se doda jos jedan
template parametar (StateSpecificData) i pokazivac tog tipa koji bi bio apstraktna klasa za klase koje
cuvaju podatke specificne za neko stanje; Change bi alocirao ove podatke kod promene stanja,
a iz f1a, f1b, f2a, f2b bi moglo da se pristupa tim podacima
(dohvatis ih sa StateSpecificData *data = selector->get_internal_data ();).
Cudo jedno sta sve moze da se napravi ovim template<vratolomijama> ;)
Nego, ima tu jos nesto... Neka me ispravi neko ko vise koristi VC++, ali mislim da
poziv COM metoda vec unosi prilican overhead tako da jedan poziv funkcije vise ili manje
ne menja bitno stvari.
Pozdrav,
Spaske