LUMIERA.clone/src/common/visitordispatcher.hpp

216 lines
5.6 KiB
C++
Raw Normal View History

/*
VISITORDISPATCHER.hpp - visitor implementation details
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef LUMIERA_VISITORDISPATCHER_H
#define LUMIERA_VISITORDISPATCHER_H
#include "common/error.hpp"
#include "common/util.hpp"
#include "common/singleton.hpp"
#include "common/multithread.hpp"
#include <vector>
namespace lumiera
{
namespace visitor
{
template<class TOOL> class Tag;
template<class TOOL, class TOOLImpl>
struct TagTypeRegistry
{
static Tag<TOOL> tag;
};
/**
* Type tag for concrete visiting tool classes.
* Used to access the previously registered dispatcher
* trampoline function when handling a visitor invocation.
*/
template<class TOOL>
class Tag
{
size_t tagID; ///< tag value
static size_t lastRegisteredID;
private:
static void
generateID (size_t& id)
{
Thread::Lock<Tag> guard SIDEEFFECT;
if (!id)
id = ++lastRegisteredID;
}
public:
Tag() : tagID(0) { }
operator size_t() const { return tagID; }
template<class TOOLImpl>
static Tag&
get (TOOLImpl* const concreteTool=0)
{
Tag& t = TagTypeRegistry<TOOL,TOOLImpl>::tag;
if (!t) generateID (t.tagID);
return t;
}
};
/** storage for the Tag registry for each concrete tool */
template<class TOOL, class TOOLImpl>
Tag<TOOL> TagTypeRegistry<TOOL,TOOLImpl>::tag;
template<class TOOL>
size_t Tag<TOOL>::lastRegisteredID (0);
/**
* For each posible call entry point via some subclass of the visitable hierarchy,
* we maintain a dispatcher table to keep track of all concrete tool implementations
* able to recieve and process calls on objects of this subclass.
*/
template<class TAR, class TOOL>
class Dispatcher
{
typedef typename TOOL::ReturnType ReturnType;
/** generator for Trampoline functions,
* used to dispatch calls down to the
* right "treat"-Function on the correct
* concrete tool implementation class
*/
template<class TOOLImpl>
static ReturnType
callTrampoline (TAR& obj, TOOL& tool)
{
// cast down to real implementation type
REQUIRE (INSTANCEOF (TOOLImpl, &tool));
TOOLImpl& toolObj = static_cast<TOOLImpl&> (tool);
// trigger (compile time) overload resolution
// based on concrete type, then dispatch the call.
// Note this may cause obj to be upcasted.
return toolObj.treat (obj);
}
typedef ReturnType (*Trampoline) (TAR&, TOOL& );
/** VTable for storing the Trampoline pointers */
std::vector<Trampoline> table_;
void
accomodate (size_t index)
{
Thread::Lock<Dispatcher> guard SIDEEFFECT;
if (index > table_.size())
table_.resize (index); // performance bottleneck?? TODO: measure the real impact!
}
inline bool
is_known (size_t id)
{
return id<=table_.size() && table_[id-1];
}
inline void
storePtr (size_t id, Trampoline func)
{
REQUIRE (func);
REQUIRE (0 < id);
if (id>table_.size())
accomodate (id);
table_[id-1] = func;
}
inline Trampoline
storedTrampoline (size_t id)
{
if (id<=table_.size() && table_[id-1])
return table_[id-1];
else
return &errorHandler;
}
static ReturnType
errorHandler (TAR& target, TOOL& tool)
{
return tool.onUnknown (target);
}
public:
static Singleton<Dispatcher<TAR,TOOL> > instance;
inline ReturnType
forwardCall (TAR& target, TOOL& tool)
{
// get concrete type via tool's VTable
Tag<TOOL> index = tool.getTag();
return (*storedTrampoline(index)) (target, tool);
}
template<class TOOLImpl>
inline void
enroll(TOOLImpl* typeref)
{
Tag<TOOL>& index = Tag<TOOL>::get (typeref);
if (is_known (index))
return;
else
{
Trampoline func = &callTrampoline<TOOLImpl>;
storePtr (index, func);
}
}
};
/** storage for the dispatcher table(s) */
template<class TAR, class TOOL>
Singleton<Dispatcher<TAR,TOOL> > Dispatcher<TAR,TOOL>::instance;
} // namespace visitor
} // namespace lumiera
#endif