Commit cf3b00e5 authored by Václav Šmilauer's avatar Václav Šmilauer
Browse files

1. Add sub-stepping functionality (step by engines rather than by whole iterations)

2. Expose Scene in python properly (not very well tested yet...)
3. Update documentation about attribute flags
4. Remove some 3rd party files no longer needed
parent 26dbbd5b
......@@ -577,7 +577,6 @@ if not env.GetOption('clean'):
if os.path.exists(boostDir+'/python'): os.remove(boostDir+'/python') # remove deprecated symlink from previous line that can break things if it is left there; remove this at some point in the future
mkSymlink(buildDir+'/include/yade-'+env['version']+'/indexing_suite','py/3rd-party/boost-python-indexing-suite-v2-noSymlinkHeaders')
mkSymlink(boostDir+'/math','extra/floating_point_utilities_v3/boost/math')
mkSymlink(boostDir+'/introspection','extra/introspection/boost/introspection')
#env.InstallAs(env['PREFIX']+'/include/yade-'+env['version']+'/boost/foreach.hpp',foreachTarget)
env.Default(env.Alias('install',['$PREFIX/bin','$PREFIX/lib'])) # build and install everything that should go to instDirs, which are $PREFIX/{bin,lib} (uses scons' Install)
......
......@@ -69,7 +69,8 @@ class ForceContainer{
* for a particular body id. */
const Vector3r& getForceUnsynced (Body::id_t id){ensureSize(id); return _force[id];}
const Vector3r& getTorqueUnsynced(Body::id_t id){ensureSize(id); return _force[id];}
friend class PhysicalActionDamperUnit;
// dummy function to avoid template resolution failure
friend class boost::serialization::access; template<class ArchiveT> void serialize(ArchiveT & ar, unsigned int version){}
public:
ForceContainer(): size(0), synced(true),moveRotUsed(false),syncCount(0){
nThreads=omp_get_max_threads();
......@@ -163,10 +164,11 @@ class ForceContainer {
std::vector<Vector3r> _rot;
size_t size;
inline void ensureSize(Body::id_t id){ if(size<=(size_t)id) resize(min((size_t)1.5*(id+100),(size_t)(id+2000)));}
friend class PhysicalActionDamperUnit;
const Vector3r& getForceUnsynced (Body::id_t id){ return getForce(id);}
const Vector3r& getTorqueUnsynced(Body::id_t id){ return getForce(id);}
bool moveRotUsed;
// dummy function to avoid template resolution failure
friend class boost::serialization::access; template<class ArchiveT> void serialize(ArchiveT & ar, unsigned int version){}
public:
ForceContainer(): size(0), moveRotUsed(false), syncCount(0){}
const Vector3r& getForce(Body::id_t id){ensureSize(id); return _force[id];}
......
......@@ -27,20 +27,13 @@
#include<unistd.h>
#include<time.h>
YADE_PLUGIN((Scene));
CREATE_LOGGER(Scene);
// should be elsewhere, probably
bool TimingInfo::enabled=false;
Scene::Scene(): bodies(new BodyContainer), interactions(new InteractionContainer), cell(new Cell){
needsInitializers=true;
iter=0;
time=0;
stopAtIter=0;
stopAtRealTime=0; // not yet implemented
stopAtVirtTime=0; // not yet implemented either
dt=1e-8;
selectedBody=-1;
isPeriodic=false;
void Scene::fillDefaultTags(){
// fill default tags
struct passwd* pw;
char hostname[HOST_NAME_MAX];
......@@ -59,6 +52,7 @@ Scene::Scene(): bodies(new BodyContainer), interactions(new InteractionContainer
void Scene::postLoad(Scene&){
// FIXME: this should be no longer necessary with boost::serialization, but it must be checked
/* since yade::serialization doesn't properly handle shared pointers, iterate over all bodies and make materials shared again, if id>=0 */
FOREACH(const shared_ptr<Body>& b, *bodies){
if(!b) continue; // erased body
......@@ -77,37 +71,47 @@ void Scene::moveToNextTimeStep(){
forces.resize(bodies->size());
needsInitializers=false;
}
if(isPeriodic) cell->integrateAndUpdate(dt);
//forces.reset(); // uncomment if ForceResetter is removed
bool TimingInfo_enabled=TimingInfo::enabled; // cache the value, so that when it is changed inside the step, the engine that was just running doesn't get bogus values
TimingInfo::delta last=TimingInfo::getNow(); // actually does something only if TimingInfo::enabled, no need to put the condition here
FOREACH(const shared_ptr<Engine>& e, engines){
e->scene=this;
if(!e->isActivated()) continue;
e->action();
if(TimingInfo_enabled) {TimingInfo::delta now=TimingInfo::getNow(); e->timingInfo.nsec+=now-last; e->timingInfo.nExec+=1; last=now;}
if(!subStepping && subStep<0){
// ** 1. ** prologue
if(isPeriodic) cell->integrateAndUpdate(dt);
//forces.reset(); // uncomment if ForceResetter is removed
bool TimingInfo_enabled=TimingInfo::enabled; // cache the value, so that when it is changed inside the step, the engine that was just running doesn't get bogus values
TimingInfo::delta last=TimingInfo::getNow(); // actually does something only if TimingInfo::enabled, no need to put the condition here
// ** 2. ** engines
FOREACH(const shared_ptr<Engine>& e, engines){
e->scene=this;
if(!e->isActivated()) continue;
e->action();
if(TimingInfo_enabled) {TimingInfo::delta now=TimingInfo::getNow(); e->timingInfo.nsec+=now-last; e->timingInfo.nExec+=1; last=now;}
}
// ** 3. ** epilogue
iter++;
time+=dt;
} else {
/* IMPORTANT: take care to copy EXACTLY the same sequence as is in the block above !! */
if(TimingInfo::enabled){ TimingInfo::enabled=false; LOG_INFO("O.timingEnabled disabled, since O.subStepping is used."); }
// ** 1. ** prologue
if(subStep<-1 || subStep>(int)engines.size()){ LOG_WARN("Invalid value of Scene::subStep ("<<subStep<<"), setting to -1 (prologue will be run)."); subStep=-1; }
if(subStep==-1){ if(isPeriodic) cell->integrateAndUpdate(dt); }
// ** 2. ** engines
else if(subStep>=0 && subStep<(int)engines.size()){ const shared_ptr<Engine>& e(engines[subStep]); e->scene=this; if(e->isActivated()) e->action(); }
// ** 3. ** epilogue
else if(subStep==(int)engines.size()){ iter++; time+=dt; /* gives -1 along with the increment afterwards */ subStep=-2; }
// (?!)
else { /* never reached */ assert(false); }
subStep++;
}
iter++;
time+=dt;
}
shared_ptr<Engine> Scene::engineByName(string s){
shared_ptr<Engine> Scene::engineByName(const string& s){
FOREACH(shared_ptr<Engine> e, engines){
if(e->getClassName()==s) return e;
}
return shared_ptr<Engine>();
}
shared_ptr<Engine> Scene::engineByLabel(string s){
FOREACH(shared_ptr<Engine> e, engines){
if(e->label==s) return e;
}
return shared_ptr<Engine>();
}
bool Scene::timeStepperPresent(){
int n=0;
FOREACH(const shared_ptr<Engine>&e, engines){ if(dynamic_cast<TimeStepper*>(e.get())) n++; }
......
......@@ -26,18 +26,10 @@
#endif
class Bound;
class Scene: public Serializable{
public:
shared_ptr<Bound> bound;
shared_ptr<BodyContainer> bodies;
vector<shared_ptr<Engine> > engines;
vector<shared_ptr<Engine> > initializers;
shared_ptr<InteractionContainer> interactions;
//! Container of shared materials. Add elements using Scene::addMaterial, not directly. Do NOT remove elements from here unless you know what you are doing!
vector<shared_ptr<Material> > materials;
//! Adds material to Scene::materials. It also sets id of the material accordingly and returns it.
int addMaterial(shared_ptr<Material> m){ materials.push_back(m); m->id=(int)materials.size()-1; return m->id; }
//! Checks that type of Body::state satisfies Material::stateTypeOk. Throws runtime_error if not. (Is called from BoundDispatcher the first time it runs)
......@@ -45,22 +37,12 @@ class Scene: public Serializable{
//! update our bound; used directly instead of a BoundFunctor, since we don't derive from Body anymore
void updateBound();
// neither serialized, nor accessible from python (at least not directly)
ForceContainer forces;
//! information on periodicity; only should be used if Scene::isPeriodic
shared_ptr<Cell> cell;
//! store for arbitrary Serializable objects; will set static parameters during deserialization (primarily for GLDraw functors which otherwise have no attribute access)
vector<shared_ptr<Serializable> > miscParams; //! tags like mp3 tags: author, date, version, description etc.
list<string> tags;
//! "hash maps" of display parameters (since yade::serialization has no support for maps, emulate it via vector of strings in format key=value)
vector<shared_ptr<DisplayParameters> > dispParams;
#ifdef YADE_GROUP_RELATION_DATA
shared_ptr<GroupRelationData> grpRelationData;
#endif
Scene();
// initialize tags (author, date, time)
void fillDefaultTags();
// advance by one iteration by running all engines
void moveToNextTimeStep();
/* Functions operating on TimeStepper; they all throw exception if there is more than 1 */
......@@ -71,45 +53,45 @@ class Scene: public Serializable{
// (de)activate TimeStepper; returns whether the operation was successful (i.e. whether a TimeStepper was found)
bool timeStepperActivate(bool activate);
shared_ptr<Engine> engineByName(string s);
shared_ptr<Engine> engineByLabel(string s);
Real dt;
long iter;
Real time;
long stopAtIter;
Real stopAtVirtTime;
Real stopAtRealTime;
bool isPeriodic;
bool needsInitializers;
// for GL selection
Body::id_t selectedBody;
shared_ptr<Engine> engineByName(const string& s);
void postLoad(Scene&);
REGISTER_ATTRIBUTES(Serializable,
(tags)
YADE_CLASS_BASE_DOC_ATTRS_CTOR(Scene,Serializable,"Object comprising the whole simulation.",
#ifdef YADE_GROUP_RELATION_DATA
(grpRelationData)
((shared_ptr<GroupRelationData>,grpRelationData,,Attr::pyHidden,"Assigns float value to all possible combinations of body group that interact."))
#endif
(engines)
(initializers)
(bodies)
(interactions)
(materials)
(miscParams)
(dispParams)
(dt)
(iter)
(time)
(stopAtIter)
(isPeriodic)
(cell)
((Real,dt,1e-8,,"Current timestep for integration."))
((long,iter,0,Attr::pyReadonly,"Current iteration (computational step) number"))
((bool,subStepping,false,,"Whether we currently advance by one engine in every step (rather than by single run through all engines)."))
((int,subStep,-1,Attr::pyReadonly,"Number of sub-step; not to be changed directly. -1 means to run loop prologue (cell integration), 0…n-1 runs respective engines (n is number of engines), n runs epilogue (increment step number and time."))
((Real,time,0,Attr::pyReadonly,"Simulation time (virtual time) [s]"))
((long,stopAtIter,0,,"Iteration after which to stop the simulation."))
#if 0
// not yet implemented
((Real,stopAtTime,0,,"Time at which to stop the simulation"))
((Real,stopAtRealTime,0,,"Time at which to stop the simulation"))
#endif
((bool,isPeriodic,false,Attr::pyReadonly,"Whether periodic boundary conditions are active."))
((bool,needsInitializers,true,Attr::pyReadonly,"Whether initializers will be run before the first step."))
((Body::id_t,selectedBody,-1,,"Id of body that is selected by the user"))
((list<string>,tags,,,"Arbitrary key=value associations (tags like mp3 tags: author, date, version, description etc.)"))
((vector<shared_ptr<Engine> >,engines,,Attr::pyHidden,"Engines sequence in the simulation."))
((vector<shared_ptr<Engine> >,initializers,,Attr::pyHidden,"Engines that will be run only once, before the first step."))
((shared_ptr<BodyContainer>,bodies,new BodyContainer,Attr::pyHidden,"Bodies contained in the scene."))
((shared_ptr<InteractionContainer>,interactions,new InteractionContainer,Attr::pyHidden,"All interactions between bodies."))
((vector<shared_ptr<Material> >,materials,,Attr::pyHidden,"Container of shared materials. Add elements using Scene::addMaterial, not directly. Do NOT remove elements from here unless you know what you are doing!"))
((shared_ptr<Bound>,bound,,Attr::pyHidden,"Bounding box of the scene (only used for rendering and initialized if needed)."))
((shared_ptr<Cell>,cell,new Cell,Attr::pyHidden,"Information on periodicity; only should be used if Scene::isPeriodic."))
((vector<shared_ptr<Serializable> >,miscParams,,Attr::pyHidden,"Store for arbitrary Serializable objects; will set static parameters during deserialization (primarily for GLDraw functors which otherwise have no attribute access)"))
((vector<shared_ptr<DisplayParameters> >,dispParams,,Attr::pyHidden,"'hash maps' of display parameters (since yade::serialization had no support for maps, emulate it via vector of strings in format key=value)"))
,
/*ctor*/ fillDefaultTags();
);
REGISTER_CLASS_AND_BASE(Scene,Serializable);
DECLARE_LOGGER;
};
REGISTER_SERIALIZABLE(Scene);
......@@ -10,10 +10,13 @@
#include "Scene.hpp"
#include "Omega.hpp"
CREATE_LOGGER(SimulationFlow);
void SimulationFlow::singleAction()
{
Scene* scene=Omega::instance().getScene().get();
if (!scene) throw logic_error("SimulationFlow::singleAction: no Scene object?!");
if(scene->subStepping) { LOG_INFO("Sub-stepping disabled when running simulation continuously."); scene->subStepping=false; }
scene->moveToNextTimeStep();
if(scene->stopAtIter>0 && scene->iter==scene->stopAtIter) setTerminate(true);
};
......
......@@ -15,6 +15,7 @@ class SimulationFlow // FIXME ; bad name
{
public:
virtual void singleAction();
DECLARE_LOGGER;
};
......
......@@ -400,7 +400,7 @@ my_latex_preamble=r'''
\usepackage{euler} % must be loaded before fontspec for the whole doc (below); this must be kept for pngmath, however
\usepackage{amsmath}
\usepackage{amsbsy}
\usepackage{mathabx}
%\usepackage{mathabx}
\usepackage{underscore}
\usepackage[all]{xy}
......
......@@ -519,16 +519,12 @@ Serialization serves to save simulation to file and restore it later. This proce
* creating class instances based solely on its name;
* knowing what classes are defined inside a particular shared library (plugin).
This functionality is provided by 3 macros and 2 virtual functions; details are provided below.
This functionality is provided by 3 macros and 4 optional methods; details are provided below.
``Serializable::preProcessAttributes``
*Optional* class virtual function. See :ref:`attributeregistration`.
Prepare attributes for being (de)serialized.
:yref:`Serializable::postProcessAttributes`
*Optional* class virtual function.
Process attributes after being (de)serialized. See :ref:`attributeregistration`.
``Serializable::preLoad``, ``Serializable::preSave``, ``Serializable::postLoad``, ``Serializable::postSave``
Prepare attributes before serialization (saving) or deserialization (loading) or process them after serialization or deserialization.
See :ref:`attributeregistration`.
``YADE_CLASS_BASE_DOC_*``
Inside the class declaration (i.e. in the ``.hpp`` file within the ``class Foo { /* … */};`` block). See :ref:`attributeregistration`.
......@@ -555,7 +551,7 @@ All (serializable) types in Yade are one of the following:
This funcionality is hidden behind the macro :ref:`YADE_CLASS_BASE_DOC` used in class declaration body (header file), which takes base class and list of attributes::
YADE_CLASS_BASE_DOC_ATTRS(ThisClass,BaseClass,"class documentation",((type1,attribute1,initValue1,"Documentation for attribute 1"))((type2,attribute2,initValue2,"Documentation for attribute 2"));
YADE_CLASS_BASE_DOC_ATTRS(ThisClass,BaseClass,"class documentation",((type1,attribute1,initValue1,,"Documentation for attribute 1"))((type2,attribute2,initValue2,,"Documentation for attribute 2"));
Note that attributes are encodes in double parentheses, not separated by commas. Empty attribute list can be given simply by ``YADE_CLASS_BASE_DOC_ATTRS(ThisClass,BaseClass,"documentation",)`` (the last comma is mandatory), or by omiting ``ATTRS`` from macro name and last parameter altogether.
......@@ -718,8 +714,8 @@ Expected parameters are indicated by macro name components separated with unders
.. code-block:: c++
((type1,attr1,initValue1,"Attribute 1 documentation"))
((type2,attr2,,"Attribute 2 documentation")) // initValue unspecified
((type1,attr1,initValue1,attrFlags,"Attribute 1 documentation"))
((type2,attr2,,,"Attribute 2 documentation")) // initValue and attrFlags unspecified
This will expand to
......@@ -738,21 +734,36 @@ Expected parameters are indicated by macro name components separated with unders
No initial value will be assigned for attribute of which initial value is left empty (as is for attr2 in the above example). Note that you still have to write the commas.
#. Registration of the attribute in the serialization system
#. Registration of the attribute in the serialization system (unless disabled by attrFlags -- see below)
#. Registration of the attribute in python, so that it can be accessed as ``klass().name1``.
The attribute will be read-write; to avoid this, override it by read-only attribute of the same name in the py section (see below).
#. Registration of the attribute in python (unless disabled by attrFlags), so that it can be accessed as ``klass().name1``.
The attribute is read-write by default, see attrFlags to change that.
This attribute will carry the docstring provided, along with knowledge of the initial value. You can add text description to the default value using the comma operator of c++ and casting the char* to (void):
.. code-block:: c++
((Real,dmgTau,((void)"deactivated if negative",-1),"Characteristic time for normal viscosity. [s]"))
((Real,dmgTau,((void)"deactivated if negative",-1),,"Characteristic time for normal viscosity. [s]"))
leading to :yref:`CpmMat::dmgTau`.
The attribute is registered via ``boost::python::add_property`` specifying ``return_by_value`` policy rather than ``return_internal_reference``, which is the default when using ``def_readwrite``. The reason is that we need to honor custom converters for those values; see note in :ref:`customconverters` for details.
.. admonition:: Attribute flags
By default, an attribute will be serialized and will be read-write from python. There is a number of flags that can be passed as the 4th argument (empty by default) to change that:
* ``Attr::noSave`` avoids serialization of the attribute (while still keeping its accessibility from Python)
* ``Attr::pyReadonly`` makes the attribute read-only from Python
* ``Attr::pyCallPostLoad`` will call ``postLoad`` function to handle attribute change after its value is set from Python; this is to ensure consistency of other precomputed data which depend on this value (such as ``Cell.trsf`` and such)
* ``Attr::pyHidden`` will not expose the attribute to Python at all
* ``Attr::pyNoResize`` will not permit changing size of the array from Python [not yet used]
Flags can be combined as usual using bitwise disjunction ``|`` (such as ``Attr::noSave | Attr::pyReadonly``), though in such case the value should be parenthesized to avoid a warning with some compilers (g++ specifically), i.e. ``(Attr::noSave | Attr::pyReadonly)``.
Currently, the flags logic handled at runtime; that means that even for attributes with ``Attr::noSave``, their serialization template must be defined (although it will never be used). In the future, the implementation might be template-based, avoiding this necessity.
``deprec``
List of deprecated attribute names. The syntax is ::
......@@ -787,7 +798,7 @@ Expected parameters are indicated by macro name components separated with unders
.. note::
The code must not contain commas ouside parentheses (since preprocessor uses commas to separate macro arguments). If you need complex things at construction time, create a separate init() function and call it from the constructor instead.
``py``
will be appeneded directly after generated python code that registers the class and all its attributes. You can use it to make accessible data member which you do not want to be serialized, or to override an already-existing attribute of the same name in order to make it read-only from python (taken from :yref:`CpmPhys`):
will be appeneded directly after generated python code that registers the class and all its attributes. You can use it to access class methods from python, for instance, to override an existing attribute with the same name etc:
.. code-block:: c++
......@@ -796,23 +807,6 @@ Expected parameters are indicated by macro name components separated with unders
``def_readonly`` will not work for custom types (such as std::vector), as it bypasses conversion registry; see :ref:`customconverters` for details.
Changing some attributes might render other data within the object inconsistent (cache coherency); for instance, :yref:`Cell` must update :yref:`Cell.size` each time :yref:`Cell.trsf` is updated, since :yref:`Cell.size` is not computed every time, but cached value is used instead. In that case, getter and setter functions must be defined, which will, besides getting/setting value of the attribute itself also run appropriate cache update function; we override refSize that was already registered; ``integrateAndUpdate(0)`` triggers cache update in this case:
.. code-block:: c++
class Cell{
public:
Vector3r getRefSize(){ return refSize; }
void setRefSize(const Vector3r& s){ refSize=s; integrateAndUpdate(0); }
YADE_CLASS_BASE_DOC_ATTRS_CTOR_PY(Cell,Serializable,"doc",
/* … */
((Vector3r,refSize,Vector3r(1,1,1),"[will be overridden below]")),
/* ctor */,
/* py */
.add_property("refSize",&Cell::getRefSize,&Cell::setRefSize,"Reference size of the cell.")
);
};
Special python constructors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
......@@ -132,7 +132,7 @@ Class reference (yade.wrapper module)
sect('Callbacks','',['BodyCallback','IntrCallback'])+
sect('Preprocessors','',['FileGenerator'])+
sect('Rendering','',['OpenGLRenderer','GlShapeFunctor','GlStateFunctor','GlBoundFunctor','GlInteractionGeometryFunctor','GlInteractionPhysicsFunctor'])+ # ,'GlShapeDispatcher','GlStateDispatcher','GlBoundDispatcher','GlInteractionGeometryDispatcher','GlInteractionPhysicsDispatcher'])+
sect('Simulation data','',['Omega','BodyContainer','InteractionContainer','ForceContainer','MaterialContainer'])
sect('Simulation data','',['Omega','BodyContainer','InteractionContainer','ForceContainer','MaterialContainer','Scene','Cell'])
+"""
Other classes
---------------
......@@ -208,7 +208,7 @@ for bib in ('references','yade-articles','yade-theses','yade-conferences'):
global writer
writer=None
for w in ['latex','html']: #['html','latex']:
for w in ['html','latex']: #['html','latex']:
if 'nolatex' in sys.argv and w=='latex': continue # skip latex build if passed nolatex (used in debian packages)
writer=w
genWrapperRst()
......
......@@ -122,7 +122,9 @@ def onBodySelect(id):
O.bodies[i.id2 if i.id1==id else i.id1].shape.highlight=True
print i.id1,i.id2,i.phys,i.geom
from yade import qt
qt.View()
qt.Controller()
try:
from yade import qt
qt.View()
qt.Controller()
except ImportError: pass
O.run(20000)
downloaded from http://www.boostpro.com/vault/index.php?action=downloadfile&filename=introspection.zip&directory=&PHPSESSID=6d7ef3a7eaa1b601dbb852e571f09be7
#ifndef __BOOS_INTROSPECTION__HAS_MEMBER_DATA_HPP__INCLUDED
#define __BOOS_INTROSPECTION__HAS_MEMBER_DATA_HPP__INCLUDED
#include <boost/mpl/bool.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/introspection/traits.hpp>
////////////////////////////////////////////////////////////////////////////////
// Generate a traits class that detect if a given type X has a member data
// named Name of type Type. This class can only detects public member data or
// member data that have been explicitly registered as 'traits visible' by the
// BOOST_INTROSPECTION_SUPPORT macro.
////////////////////////////////////////////////////////////////////////////////
#define BOOST_HAS_MEMBER_DATA(Type,Name) \
namespace boost \
{ \
namespace introspection \
{ \
template<class T> struct BOOST_PP_CAT(has_member_data_,Name) \
{ \
typedef char NotFound; \
struct Found { char x[2]; }; \
\
template< class X, Type X::*> struct member {}; \
\
template<class X> static Found test(member<X,&X::Name>*); \
template<class X> static NotFound test( ... ); \
\
static const bool value = (sizeof(Found) == sizeof(test<T>(0))); \
typedef mpl::bool_<value> type; \
}; \
} \
} \
/**/
////////////////////////////////////////////////////////////////////////////////
// Used in a class, make the member data Name visible by the boost::introspection
// traits class.
////////////////////////////////////////////////////////////////////////////////
#define BOOST_INTROSPECTION_SUPPORT(Class,Name) \
friend class boost::introspection::BOOST_PP_CAT(has_member_data_,Name)<Class>\
/**/
#endif
#ifndef __BOOS_INTROSPECTION__HAS_MEMBER_FUNCTION_HPP__INCLUDED
#define __BOOS_INTROSPECTION__HAS_MEMBER_FUNCTION_HPP__INCLUDED
#include <boost/mpl/bool.hpp>
#include <boost/mpl/or.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/introspection/traits.hpp>
////////////////////////////////////////////////////////////////////////////////
// Generate a traits class that detect if a given type X has a non-const member
// function named Name with a given signature Sig.
////////////////////////////////////////////////////////////////////////////////
#define BOOST_HAS_NON_CONST_MEMBER_FUNCTION(Name,Sig) \
namespace boost \
{ \
namespace introspection \
{ \
template<class T> \
struct BOOST_PP_CAT(has_non_const_member_function_,Name) \
{ \
typedef char NotFound; \
struct Found { char x[2]; }; \
\
template< class X \
, typename build_member_type<X,Sig>::type \
> struct member {}; \
\
template<class X> static Found test(member<X,&X::Name>*); \
template<class X> static NotFound test( ... ); \
\
static const bool value = (sizeof(Found) == sizeof(test<T>(0))); \
typedef mpl::bool_<value> type; \
}; \
} \
} \
/**/
////////////////////////////////////////////////////////////////////////////////
// Generate a traits class that detect if a given type X has a const member
// function named Name with a given signature Sig.
////////////////////////////////////////////////////////////////////////////////
#define BOOST_HAS_CONST_MEMBER_FUNCTION(Name,Sig) \
namespace boost \
{ \
namespace introspection \
{ \
template<class T> \
struct BOOST_PP_CAT(has_const_member_function_,Name) \
{ \
typedef char NotFound; \
struct Found { char x[2]; }; \
\
template< class X \
, typename build_const_member_type<X,Sig>::type \
> struct member {}; \
\
template<class X> static Found test(member<X,&X::Name>*); \
template<class X> static NotFound test( ... ); \
\
static const bool value = (sizeof(Found) == sizeof(test<T>(0))); \
typedef mpl::bool_<value> type; \
}; \
} \
}
/**/
////////////////////////////////////////////////////////////////////////////////
// Generate a traits class that detect if a given type X has a member function
// named Name with a given signature Sig which is either const or non-const
////////////////////////////////////////////////////////////////////////////////
#define BOOST_HAS_MEMBER_FUNCTION(Name,Sig) \
BOOST_HAS_CONST_MEMBER_FUNCTION(Name,Sig) \
BOOST_HAS_NON_CONST_MEMBER_FUNCTION(Name,Sig) \
\
namespace boost \
{ \
namespace introspection \
{ \
template<class T> \
struct BOOST_PP_CAT(has_member_function_,Name) : \
mpl::or_< BOOST_PP_CAT(has_const_member_function_,Name)<T> \
, BOOST_PP_CAT(has_non_const_member_function_,Name)<T> \
> {}; \
} \
} \
/**/
#endif
#ifndef __BOOS_INTROSPECTION__TRAITS_HPP__INCLUDED
#define __BOOS_INTROSPECTION__TRAITS_HPP__INCLUDED
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/member_function_pointer.hpp>
#include <boost/function_types/property_tags.hpp>
namespace boost
{
namespace introspection
{
////////////////////////////////////////////////////////////////////////////
// Build a MPL sequence correspondign to the components of a member function
// type of class X with signature similar to Prototype.
// E.g :
// function_to_member<foo, void(int,long)>::type => <void,foo*,int,long>
//
////////////////////////////////////////////////////////////////////////////
template<class X, class Prototype> struct function_to_member
{