"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:464353d6$0$7609$157c6196@dreader2.cybercity.dk...
[8<8<8<]
> Det er faktisk muligt at skrive noget template kode, som automatisk
> genererer optimal hurtig kode.
Nedenstående kode tager udgangspunkt i noget kode jeg skrev i 2001, som svar
på et spørgsmål Scott Meyers stillede
http://kortlink.dk/3uu6
Lige et par advarsler:
* det er ikke trivielt
* det bruger ikke virtuelle metoder, men sin egen implementer run-time
type information med det formål at sikre hurtig look-up
* det er problematisk i forbindelse med dynamisk linkning
* jeg vil ikke anbefale at du skriver det af som opgave løsning - det som
står i Alexandrescu's bog er mere lige-ud-af-landevejen
typelist er essentiel for at lave en fornuftig løsning - og det er ikke
noget man selv opfinder fra bunden
Designet indeholder et par forskellige uafhængige elementer:
* typelist - til at specificere et container af typer. Rimeligt
almindeligt i template meta programmering
* et framework, der automatisk sikrer at hver klasse får sig unikke,
automatisk genereret ID i form af en integer
* et framework, der automatisk opbygger en 2-dimensionel jump-tabel på
run-time (det kan vist ikke gøres på compile-time)
* der er inkluderet policy klasser til at kunne bestemme hvordan de
nødvendige cast skal laves og hvad der skal ske hvis man laver dual-dispatch
på en ukendt klasse
* applikations kode, med klasser der benytter frameworket til sikre unikke
ID'er
* applikations kode der starter dual-dispatch kaldet
* applikations kode der eksekverer funktionalitet efter typerne er bestemt
Bemærk også at containeren indeholder pointere til basis-typen - altså en
homogen container.
Koden er testet med Visual C++ 2005 og Borland C++V6 på MS-Windows.
Du kan prøve at single steppe gennem koden for at finde ud af hvad der sker
#include <iostream>
#include <vector>
#include <typeinfo>
#include <utility>
#include <cassert>
// Some typelist stuff
// Very much like
// Modern C++ Design
// Andrei Alexandrescu
// ISBN 0-201-70431
// but without preprocessor macros
class null_type;
template <typename T, typename U>
struct type_list_impl
{
typedef T head;
typedef U tail;
};
template <typename T1, typename T2 = null_type, typename T3 = null_type,
typename T4 = null_type>
struct type_list;
template <typename T1, typename T2, typename T3>
struct type_list<T1, T2, T3, null_type>
{
typedef type_list_impl<T1, typename type_list<T2, T3>::list >
list;
};
template <typename T1, typename T2>
struct type_list<T1, T2, null_type>
{
typedef type_list_impl<T1, typename type_list<T2>::list >
list;
};
template <typename T1>
struct type_list<T1, null_type, null_type>
{
typedef type_list_impl<T1, null_type>
list;
};
//external polymorphic framework
template <typename T>
class external_polymorphic
{
public:
unsigned id() const;
static unsigned max_class_id();
protected:
external_polymorphic(unsigned id);
static unsigned register_class();
private:
static unsigned& class_count();
private:
const unsigned id_;
};
template <typename T, typename D>
class reg_external_polymorphic : public T
{
public:
static unsigned class_id();
protected:
reg_external_polymorphic();
template <typename ARG1>
reg_external_polymorphic(ARG1 arg1);
template <typename ARG1, typename ARG2>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2);
template <typename ARG1, typename ARG2, typename ARG3>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2, ARG3 arg3);
template <typename ARG1, typename ARG2, typename ARG3, typename ARG4>
reg_external_polymorphic(ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4);
~reg_external_polymorphic();
private:
// ensure that class_id is called, at latest before main is entered
// so that all classes are registered
static const unsigned class_id_;
};
template <typename T, typename D>
const unsigned reg_external_polymorphic<T,D>::class_id_ =
reg_external_polymorphic<T,D>::class_id();
template <typename T>
inline external_polymorphic<T>::external_polymorphic(unsigned id) :
id_(id)
{
T* t = static_cast<T*>(this);
t; // used
}
template <typename T>
inline unsigned external_polymorphic<T>::id() const
{
return id_;
}
template <typename T>
inline unsigned external_polymorphic<T>::max_class_id()
{
return class_count();
}
template <typename T>
inline unsigned& external_polymorphic<T>::class_count()
{
static unsigned class_count_ = 0;
return class_count_;
}
// made non-inline due to bug in C++Builder V6.0 code generation
template <typename T>
inline unsigned external_polymorphic<T>::register_class()
{
return class_count()++;
}
template <typename T, typename D>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic() :
T(class_id())
{
}
template <typename T, typename D>
template <typename ARG1>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1) :
T(class_id(), arg1)
{
}
template <typename T, typename D>
template <typename ARG1, typename ARG2>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2) :
T(class_id(), arg1, arg2)
{
}
template <typename T, typename D>
template <typename ARG1, typename ARG2, typename ARG3>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2, ARG3 arg3) :
T(class_id(), arg1, arg2, arg3)
{
}
template <typename T, typename D>
template <typename ARG1, typename ARG2, typename ARG3, typename ARG4>
inline reg_external_polymorphic<T, D>::reg_external_polymorphic(ARG1 arg1,
ARG2 arg2, ARG3 arg3, ARG4 arg4) :
T(class_id(), arg1, arg2, arg3, arg4)
{
}
template <typename T, typename D>
inline reg_external_polymorphic<T, D>:
reg_external_polymorphic()
{
// D must be derived from this class
reg_external_polymorphic<T, D>* d = static_cast<D*>(0);
d; //used!
// This class must be derived from external_polymorphic<T>
external_polymorphic<T>* ept = this;
ept; //used!!
&class_id_; // ensure class_id_ is reference and not optimized away !
}
template <typename T, typename D>
inline unsigned reg_external_polymorphic<T, D>::class_id()
{
static const unsigned class_id_ =
external_polymorphic<T>::register_class();
return class_id_;
}
// "external polymorphism"
// see Bjarne Stroustrup slides
// "Design Decisions for a Library"
//
http://www.klid.dk/arrangementer/XTI_kbh.pdf
// slide 14-18
//
// this vcall template class is modelled after those slides
// cast policy classes
template <typename T1>
struct do_static_cast
{
template <typename T2>
T2 do_cast(T1 t) const
{ return static_cast<T2>(t); }
};
template <typename T1>
struct do_dynamic_cast
{
template <typename T2>
T2 do_cast(T1 t) const
{ return dynamic_cast<T2>(t); }
};
// pure virtual function call policy
template <typename ReturnType>
struct pure_virtual_throw
{
template <typename ARG0, typename ARG1>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/)
{ throw 0; /* or something more sensible */ }
};
template <typename ReturnType>
struct pure_virtual_nothing
{
template <typename ARG0, typename ARG1>
static ReturnType pure_virtual(ARG0 /*arg0*/, ARG1 /*arg1*/)
{ }
};
template <typename Func, typename T1, typename T2, typename CastPolicy>
struct dual_dispatch_table_generator_impl;
template <typename Func, typename DispatchTypelist, typename CastPolicy>
struct dual_dispatch_table_generator
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
static void fill_func_vector(func_vector_type& func_vector)
{ dual_dispatch_table_generator_impl<Func, typename
DispatchTypelist::list, typename DispatchTypelist::list,
CastPolicy>::fill_func_vector(func_vector); }
static unsigned max_class_id()
{ return dual_dispatch_table_generator_impl<Func, typename
DispatchTypelist::list, typename DispatchTypelist::list,
CastPolicy>::max_class_id(); }
};
template <typename Func, typename T1, typename T2, typename CastPolicy>
struct dual_dispatch_table_generator_impl<Func, type_list_impl<T1,
null_type>, type_list_impl<T2, null_type>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
static return_type do_call(param_type const& param_1, param_type const&
param_2)
{ return Func()(CastPolicy().template do_cast<T1 const&>(param_1),
CastPolicy().template do_cast<T2 const&>(param_2)); }
static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T1::class_id()][T2::class_id()] = do_call;
}
static unsigned max_class_id()
{ return T1::max_class_id(); }
};
template <typename Func, typename T1, typename T2, typename U2, typename
CastPolicy>
struct dual_dispatch_table_generator_impl<Func, type_list_impl<T1,
null_type>, type_list_impl<T2, U2>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
static return_type do_call(param_type const& param_1, param_type const&
param_2)
{ return Func()(CastPolicy().template do_cast<T1 const&>(param_1),
CastPolicy().template do_cast<T2 const&>(param_2)); }
static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T1::class_id()][T2::class_id()] = do_call;
dual_dispatch_table_generator_impl<Func, type_list_impl<T1,
null_type>, U2, CastPolicy>::fill_func_vector(func_vector);
}
static unsigned max_class_id()
{
return T1::max_class_id();
}
};
template <typename Func, typename T1, typename U1, typename T2, typename
CastPolicy>
struct dual_dispatch_table_generator_impl<Func, type_list_impl<T1, U1>,
type_list_impl<T2, null_type>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
static return_type do_call(param_type const& param_1, param_type const&
param_2)
{ return Func()(CastPolicy().template do_cast<T1 const&>(param_1),
CastPolicy().template do_cast<T2 const&>(param_2)); }
static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T1::class_id()][T2::class_id()] = do_call;
dual_dispatch_table_generator_impl<Func, U1, type_list_impl<T2,
null_type>, CastPolicy>::fill_func_vector(func_vector);
}
static unsigned max_class_id()
{
return T1::max_class_id();
}
};
template <typename Func, typename T1, typename U1, typename T2, typename U2,
typename CastPolicy>
struct dual_dispatch_table_generator_impl<Func, type_list_impl<T1, U1>,
type_list_impl<T2, U2>, CastPolicy>
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
static return_type do_call(param_type const& param_1, param_type const&
param_2)
{ return Func()(CastPolicy().template do_cast<T1 const&>(param_1),
CastPolicy().template do_cast<T2 const&>(param_2)); }
static void fill_func_vector(func_vector_type& func_vector)
{
func_vector[T1::class_id()][T2::class_id()] = do_call;
dual_dispatch_table_generator_impl<Func, type_list_impl<T1,
null_type>, U2, CastPolicy>::fill_func_vector(func_vector);
dual_dispatch_table_generator_impl<Func, U1, type_list_impl<T2,
null_type>, CastPolicy>::fill_func_vector(func_vector);
dual_dispatch_table_generator_impl<Func, U1, U2,
CastPolicy>::fill_func_vector(func_vector);
dual_dispatch_table_generator_impl<Func, U2, U1,
CastPolicy>::fill_func_vector(func_vector);
}
static unsigned max_class_id()
{
return T1::max_class_id();
}
};
template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy, typename CastPolicy>
struct dual_dispatch_func_table
{
typedef typename Func::return_type return_type;
typedef typename Func::param_type param_type;
typedef return_type (*func_ptr_type)(param_type const&, param_type
const&);
typedef std::vector<std::vector<func_ptr_type> > func_vector_type;
typedef dual_dispatch_table_generator<Func, DispatchTypelist, CastPolicy>
dual_dispatch_table_generator_type;
dual_dispatch_func_table();
func_vector_type const& func_vector() const
{ return func_vector_; }
static return_type pure_virtual(param_type const& param_1, param_type
const& param_2)
{ return PureVirtualPolicy().pure_virtual<param_type const&, param_type
const&>(param_1, param_2); }
static unsigned max_class_id()
{ return dual_dispatch_table_generator_type::max_class_id(); }
private:
func_vector_type func_vector_;
private:
dual_dispatch_func_table(dual_dispatch_func_table const&);
dual_dispatch_func_table& operator=(dual_dispatch_func_table const&);
};
// constructor is not inlined - only called once
// code bloat if inlined
template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy, typename CastPolicy>
dual_dispatch_func_table<Func, DispatchTypelist, PureVirtualPolicy,
CastPolicy>::dual_dispatch_func_table() :
func_vector_(max_class_id(), std::vector<func_ptr_type>(max_class_id(),
pure_virtual))
{
dual_dispatch_table_generator_type::fill_func_vector(func_vector_);
}
template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy, typename CastPolicy>
inline typename Func::return_type dual_dispatch_call(typename
Func::param_type const& param_1, typename Func::param_type const& param_2)
{
typedef typename dual_dispatch_func_table<Func, DispatchTypelist,
PureVirtualPolicy, CastPolicy> dual_dispatch_func_table_type;
typedef typename dual_dispatch_func_table_type::func_vector_type
func_vector_type;
// jump-table initialized first time dual_dispatch_call is called
static const dual_dispatch_func_table_type func_tbl;
return (*func_tbl.func_vector()[param_1.id()][param_2.id()])(param_1,
param_2);
}
template <typename Func, typename DispatchTypelist>
inline typename Func::return_type dual_dispatch_call(typename
Func::param_type const& param_1, typename Func::param_type const& param_2)
{
return dual_dispatch_call<Func, DispatchTypelist,
pure_virtual_throw<typename Func::return_type>, do_static_cast<typename
Func::param_type const&> >(param_1, param_2);
}
template <typename Func, typename DispatchTypelist, typename
PureVirtualPolicy>
inline typename Func::return_type dual_dispatch_call(typename
Func::param_type const& param_1, typename Func::param_type const& param_2)
{
return dual_dispatch_call<Func, DispatchTypelist, PureVirtualPolicy,
do_static_cast<typename Func::param_type const&> >(param_1, param_2);
}
// application code
// The geometry framework
class geometry : public external_polymorphic<geometry>
{
// the destructor _need_ not be virtual
// we do not rely on RTTI
protected:
// destructor made protected to prevent
geometry(unsigned id) :
external_polymorphic<geometry>(id) {}
private:
// copy constructor and copy assignment not implemented
geometry(const geometry&);
geometry& operator=(const geometry&);
};
class sphere : public reg_external_polymorphic<geometry, sphere>
{
public:
sphere() {}
};
class box : public reg_external_polymorphic<geometry, box>
{
public:
box() {}
};
class tetrahedron : public reg_external_polymorphic<geometry, tetrahedron>
{
public:
tetrahedron() {}
};
template <typename BaseT>
struct func_process
{
public:
typedef BaseT param_type;
typedef void return_type;
template <typename DerivedT>
return_type operator()(DerivedT const& t)
{ return process(t); }
template <typename DerivedT1, typename DerivedT2>
return_type operator()(DerivedT1 const& param_1, DerivedT2 const&
param_2)
{ return process(param_1, param_2); }
};
inline void process(geometry const& param_1, geometry const& param_2)
{
typedef type_list<
sphere,
box,
tetrahedron>
geometry_type_list;
typedef func_process<geometry> func;
return dual_dispatch_call<func, geometry_type_list>(param_1, param_2);
}
template <typename T1, typename T2>
void process(T1 const& /*t_1*/, T2 const& /*t_2*/)
{
std::cout << "General: " << typeid(T1).name() << "<->" <<
typeid(T2).name() << std::endl;
}
void process(sphere const& sphere_obj, box const& box_obj)
{
std::cout << "Special sphere<->box" << std::endl;
}
template <typename T>
std::vector<std::pair<T*, T*> > broad_phase(const std::vector<T*>& scene)
{
using std::vector;
using std::pair;
using std::make_pair;
const vector<T*>::size_type scene_size = scene.size();
vector<pair<T*, T*> > result;
result.reserve(scene_size * (scene_size-1));
for(vector<T*>::const_iterator i = scene.begin(); scene.end() != i; ++i)
{
for(vector<T*>::const_iterator j = i + 1; scene.end() != j; ++j) {
result.push_back(make_pair(*i, *j));
}
}
return result;
}
template <typename T>
void process(const std::vector<T*>& scene)
{
using std::vector;
using std::pair;
const vector<pair<T*, T*> > scene_pair = broad_phase(scene);
for(vector<pair<T*, T*> >::const_iterator i = scene_pair.begin();
scene_pair.end() != i; ++i) {
assert(i->first);
assert(i->second);
process(*i->first, *i->second);
}
}
int main()
{
using std::cerr; using std::cout;
using std::endl; using std::vector;
sphere geo_1;
box geo_2;
tetrahedron geo_3;
vector<geometry*> scene;
scene.push_back(&geo_1);
scene.push_back(&geo_2);
scene.push_back(&geo_3);
scene.push_back(&geo_1); // allow processing of same types
scene.push_back(&geo_2);
scene.push_back(&geo_3);
process(scene);
}
--
Venlig hilsen
Mogens Hansen