Skip to content
Snippets Groups Projects
Commit 20d8b91c authored by Thomas Moulard's avatar Thomas Moulard
Browse files

Clean and document factoryStorage.

parent 6844f57c
No related branches found
No related tags found
No related merge requests found
......@@ -30,6 +30,7 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/import-default-paths.h
# Headers list.
SET(${PROJECT_NAME}_HEADERS
fwd.hh
contiifstream.h
debug.h
......
/*
* Copyright 2010,
* François Bleibel,
* Olivier Stasse,
*
* CNRS/AIST
*
* This file is part of dynamic-graph.
* dynamic-graph is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* dynamic-graph 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 Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FACTORY_HH__
#define __FACTORY_HH__
/* --------------------------------------------------------------------- */
/* --- INCLUDE --------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/* --- STD --- */
#include <map>
#include <string>
#include <vector>
/* --- DYNAMIC-GRAPH --- */
#include <dynamic-graph/exception-factory.h>
#include <dynamic-graph/dynamic-graph-api.h>
namespace dynamicgraph {
class Entity;
/* --------------------------------------------------------------------- */
/* --- FACTORY ---------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/*! \class FactoryStorage
* \ingroup dgraph
* \brief The Factory class is responsible for creating Entity objects.
*
* Entities can register themselves through the helper class EntityRegisterer.
* This object also provides access to command-line functions.
*
*
*/
class DYNAMIC_GRAPH_DLLAPI FactoryStorage
{
public:
typedef Entity* (*EntityConstructor_ptr)( const std::string& );
protected:
typedef std::map< std::string,EntityConstructor_ptr > EntityMap;
EntityMap entityMap;
public:
~FactoryStorage( void );
void registerEntity( const std::string& entname,EntityConstructor_ptr ent );
void deregisterEntity( const std::string& entname );
Entity* newEntity( const std::string& name,const std::string& objname );
bool existEntity( const std::string& name, EntityMap::iterator& entPtr );
bool existEntity( const std::string& name );
/// Return the list of Entity class names registered in the factory.
/// Class names are appended at end of output vector.
void listEntities(std::vector <std::string>& outList);
void commandLine( const std::string& cmdLine,std::istringstream& cmdArgs,
std::ostream& os );
};
DYNAMIC_GRAPH_DLLAPI extern FactoryStorage g_factory;
/* --- REGISTERER ----------------------------------------------------------- */
/* --- REGISTERER ----------------------------------------------------------- */
/* --- REGISTERER ----------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* --- ENTITY REGISTERER ---------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/*! A helper class to register an entity.
*
*/
class DYNAMIC_GRAPH_DLLAPI EntityRegisterer
{
private:
EntityRegisterer( void );
std::string entityName;
public:
EntityRegisterer( const std::string& entityClassName,
FactoryStorage::EntityConstructor_ptr maker);
~EntityRegisterer( void );
};
} // namespace dynamicgraph
/*! This macro should be used to automatically register an entity
* of classType to the g_factory. It is then possible to create it
* with the g_factory.
*/
#define DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(CLASSTYPE, CLASSNAME) \
// -*- c++-mode -*-
// Copyright 2010, François Bleibel, Thomas Moulard, Olivier Stasse,
// JRL, CNRS/AIST.
//
// This file is part of dynamic-graph.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph 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 Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#ifndef DYNAMIC_GRAPH_FACTORY_HH
# define DYNAMIC_GRAPH_FACTORY_HH
# include <map>
# include <string>
# include <vector>
# include <boost/noncopyable.hpp>
# include <dynamic-graph/fwd.hh>
# include <dynamic-graph/exception-factory.h>
# include <dynamic-graph/dynamic-graph-api.h>
/// \brief Automatically register a class to the global factory
/// by relying on the static initialization.
///
/// \param CLASSTYPE the Entity type to be registered
/// \param CLASSNAME the name of the Entity to be registered (this must
/// be a std::string or a type implicitly castable into a std::string
/// such as classic C string delimited by double quotes).
# define DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN(CLASSTYPE, CLASSNAME) \
const std::string CLASSTYPE::CLASS_NAME = CLASSNAME; \
extern "C" { \
::dynamicgraph::Entity* \
......@@ -122,8 +49,188 @@ class DYNAMIC_GRAPH_DLLAPI EntityRegisterer
} \
struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n
#endif /* #ifndef __FACTORY_HH__ */
namespace dynamicgraph
{
/// \ingroup dgraph
///
/// \brief Provides a way to create Entity objects from their class
/// name.
///
/// The dynamic graph frameworks relies on entities (see Entity)
/// which defines atomic processing units. This class provides a
/// robust way to enumerate and instantiate these entities.
/// Each entity has a name (its type name) and can be instantiated.
/// Each instance also has a name.
///
/// For instance one can define a C++ class called MyEntity which
/// inherits from dynamicgraph::Entity. This type can be registered
/// into the factory to teach the framework that:
/// - this entity exists
/// - this entity can be instantiated (and how to instantiate it).
///
/// To achieve this, one must pass an entity name and a function pointer.
///
/// The entity name will identify the class <b>at run-time</b>
/// (be careful: this may not be equivalent to the C++ class name
/// even if it is recommended to do so).
///
/// The function pointer must point on a function taking a string as
/// input and returning an instance of the Entity (the concrete
/// subclass, not directly the upper Entity class).
///
/// The instances returned by this function <b>must</b> be
/// dynamically allocated and the caller <b>must</b> get the
/// ownership of the instance (i.e. it will free it when required).
///
/// To finish, please note that the instance name indicates to the
/// entity how the instance itself is called at run-time. This name
/// does not need to be unique and no check is done on it. It is
/// the caller responsibility to make sure that the instance name is
/// appropriate and to check for uniqueness if required.
///
///
/// This class should <b>never</b> be used directly. Use the
/// g_factory global variable instead. The rationale is that each
/// unique name must identify a unique Entity. The use of a single
/// instance of this class enforces this behavior, instantiating one
/// yourself would break this property.
class DYNAMIC_GRAPH_DLLAPI FactoryStorage : private boost::noncopyable
{
public:
/// \brief Function pointer providing an entity instance from its
/// name.
typedef Entity* (*EntityConstructor_ptr) (const std::string&);
/// \brief Constructor the factory.
///
/// After the initialization, no entities will be available.
/// registerEntity has to be used to add new entities to the
/// object.
explicit FactoryStorage ();
~FactoryStorage ();
/// \brief Add a new entity to the factory.
///
/// It is not allowed to have several entities using the same
/// name. If this is the case, an ExceptionFactory exception will
/// be raised with the code OBJECT_CONFLICT.
///
/// If the function pointer is null, an ExceptionFactory exception
/// will be raised with the code OBJECT_CONFLICT.
///
/// \param entname the name used to subscribe the entity.
/// \param pointer to a function allocating an entity from an
/// instance name.
void registerEntity (const std::string& entname,
EntityConstructor_ptr ent);
/// \brief Delete an entity from the factory.
///
/// If the provided entity name does not exist in the factory,
/// an ExceptionFactory exception will be raised with the code
/// OBJECT_CONFLICT.
///
/// \param entname the entity name (as passed to registerEntity before)
void deregisterEntity (const std::string& entname);
/// \brief Instantiate (and allocate) an entity.
///
/// An instance called objname of the entity which type is classname
/// will be allocated by this method.
///
/// It is <b>the caller</b> responsibility to free the
/// returned object.
///
/// If the class name does not exist, an ExceptionFactory
/// exception will be raised with the code UNREFERED_OBJECT.
///
/// The instance name (objname) is passed to the Entity
/// constructor and it is the caller responsibility to avoid
/// instance name conflicts if necessary.
///
/// \param classname the name of the Entity type
/// \param objname the instance name
/// \return Dynamically allocated instance of classname.
Entity* newEntity (const std::string& classname,
const std::string& objname) const;
/// \brief Check if an Entity associated with a particular name
/// has already been registered.
///
/// \param name entity name
/// \return Do the entity exist?
bool existEntity (const std::string& name) const;
/// \brief List the available entities.
///
/// Available entities are appended to the method argument.
///
/// \param list Available entities will be appended to list.
void listEntities (std::vector <std::string>& list) const;
/// \brief Define FactoryStorage commands.
///
/// Define two equivalent commands:
/// - list
/// - listEntities
/// listing the available entities.
void commandLine (const std::string& cmdLine,
std::istringstream& cmdArgs,
std::ostream& os);
private:
/// \brief Entity map type.
///
/// This maps entity names to functions pointers which can be
/// used to instantiate an Entity.
typedef std::map<std::string, EntityConstructor_ptr> EntityMap;
/// \brief The entity map storing information about how to
/// instantiate an Entity.
EntityMap entityMap;
};
/// \brief Global factory.
///
/// This global variable is the only valid instance of the
/// FactoryStorage class.
///
/// This unique instance is to make sure that only only one entity
/// list is built.
///
/// This global variable must be used to search for entities and
/// to instantiate them.
///
/// Please use the DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN macro if
/// possible to register an entity to this factory.
DYNAMIC_GRAPH_DLLAPI extern FactoryStorage g_factory;
/// \brief This class automatically register an Entity to the
/// global factory at initialization and unregister it during
/// instance destruction.
///
/// This class is mainly used by the
/// DYNAMICGRAPH_FACTORY_ENTITY_PLUGIN macro and is of little interest
/// by itself.
class DYNAMIC_GRAPH_DLLAPI EntityRegisterer : private boost::noncopyable
{
public:
/// \brief Register entity to the global factory.
explicit EntityRegisterer (const std::string& entityClassName,
FactoryStorage::EntityConstructor_ptr maker);
/// \brief Unregister entity to the global factory.
~EntityRegisterer ();
private:
/// \brief Name of the entity registered when the instance has
/// been initialized.
const std::string entityName;
};
} // namespace dynamicgraph
#endif //! DYNAMIC_GRAPH_FACTORY_HH
// LocalWords: unregister
// Copyright 2010, Thomas Moulard, JRL, CNRS/AIST
//
// This file is part of dynamic-graph.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph 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 Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#ifndef DYNAMIC_GRAPH_FWD_HH
# define DYNAMIC_GRAPH_FWD_HH
namespace dynamicgraph
{
class Entity;
class FactoryStorage;
class EntityRegisterer;
} // end of namespace dynamicgraph.
#endif //! DYNAMIC_GRAPH_FWD_HH
/*
* Copyright 2010,
* François Bleibel,
* Olivier Stasse,
*
* CNRS/AIST
*
* This file is part of dynamic-graph.
* dynamic-graph is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
* dynamic-graph 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 Lesser General Public License for more details. You should
* have received a copy of the GNU Lesser General Public License along
* with dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
*/
/* --------------------------------------------------------------------- */
/* --- INCLUDE --------------------------------------------------------- */
/* --------------------------------------------------------------------- */
/* --- DYNAMIC-GRAPH --- */
#include <dynamic-graph/debug.h>
#include <dynamic-graph/factory.h>
// Copyright 2010, François Bleibel, Thomas Moulard, Olivier Stasse,
// JRL, CNRS/AIST.
//
// This file is part of dynamic-graph.
// dynamic-graph is free software: you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// dynamic-graph 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 Lesser Public License for more details. You should have
// received a copy of the GNU Lesser General Public License along with
// dynamic-graph. If not, see <http://www.gnu.org/licenses/>.
#include <boost/foreach.hpp>
#include "dynamic-graph/debug.h"
#include "dynamic-graph/factory.h"
using namespace std;
using namespace dynamicgraph;
/* --------------------------------------------------------------------- */
/* --- CLASS ----------------------------------------------------------- */
/* --------------------------------------------------------------------- */
namespace dynamicgraph
{
FactoryStorage::FactoryStorage ()
: entityMap ()
{}
FactoryStorage::~FactoryStorage ()
{
dgDEBUGINOUT (25);
}
FactoryStorage::
~FactoryStorage( void )
{
dgDEBUGINOUT(25);
}
void
FactoryStorage::registerEntity(const std::string& entname,
FactoryStorage::EntityConstructor_ptr ent)
{
dgDEBUGIN (25);
if (existEntity (entname))
{
DG_THROW ExceptionFactory
(ExceptionFactory::OBJECT_CONFLICT,
"Another entity class already defined with the same name. ",
"(while adding entity class <%s> inside the g_factory).",
entname.c_str ());
dgERRORF ("Another entity class already defined with the same name. "
"(while adding entity class <%s> inside the factory).",
entname.c_str ());
}
else
{
if (!ent)
{
//FIXME: we should have a better error code for that.
DG_THROW ExceptionFactory
(ExceptionFactory::OBJECT_CONFLICT,
"Bad entity constructor.");
}
dgDEBUG (30) << "Register entity <"<< entname
<< "> in the factory." <<std::endl;
entityMap[entname] = ent;
}
dgDEBUGOUT (25);
}
void
FactoryStorage::deregisterEntity (const std::string& entname)
{
dgDEBUGIN (25);
if (!existEntity(entname))
{
DG_THROW ExceptionFactory( ExceptionFactory::OBJECT_CONFLICT,
"Entity class not defined yet. ",
"(while removing entity class <%s>).",
entname.c_str() );
dgERRORF(ExceptionFactory::OBJECT_CONFLICT,
"Entity class not defined yet. "
"(while removing entity class <%s>).",
entname.c_str() );
}
else
{
dgDEBUG (30) << "Deregister entity <"<< entname
<< "> from the factory." <<std::endl;
entityMap.erase(entname);
}
dgDEBUGOUT (25);
}
Entity*
FactoryStorage::newEntity (const std::string& classname,
const std::string& objname) const
{
dgDEBUG (15) << "New <" << classname << ">Entity <"
<< objname << ">" << std::endl;
EntityMap::const_iterator entPtr = entityMap.find (classname);
if (entPtr == entityMap.end ())
{
DG_THROW ExceptionFactory
(ExceptionFactory::UNREFERED_OBJECT,
"Unknown entity.",
" (while calling new_entity <%s>)",
classname.c_str ());
}
return entPtr->second (objname);
}
/* --------------------------------------------------------------------- */
void FactoryStorage::
registerEntity( const std::string& entname,FactoryStorage::EntityConstructor_ptr ent )
{
dgDEBUGIN(25);
EntityMap::iterator entkey;
if( existEntity(entname,entkey) ) // key does exist
{
// DG_THROW ExceptionFactory( ExceptionFactory::OBJECT_CONFLICT,
// "Another entity class already defined with the same name. ",
// "(while adding entity class <%s> inside the g_factory).",
// entname.c_str() );
dgERRORF("Another entity class already defined with the same name. "
"(while adding entity class <%s> inside the factory).",
entname.c_str() );
}
else
{
dgDEBUG(30) << "Register entity <"<< entname
<< "> in the factory." <<std::endl;
entityMap[entname] = ent;
}
dgDEBUGOUT(25);
}
void FactoryStorage::
deregisterEntity( const std::string& entname )
{
dgDEBUGIN(25);
EntityMap::iterator entkey;
if(! existEntity(entname,entkey) ) // key does not exist
{
DG_THROW ExceptionFactory( ExceptionFactory::OBJECT_CONFLICT,
"Entity class not defined yet. ",
"(while removing entity class <%s>).",
entname.c_str() );
dgERRORF(ExceptionFactory::OBJECT_CONFLICT,
"Entity class not defined yet. "
"(while removing entity class <%s>).",
entname.c_str() );
}
else
{
dgDEBUG(30) << "Deregister entity <"<< entname
<< "> from the factory." <<std::endl;
entityMap.erase( entkey );
}
dgDEBUGOUT(25);
}
Entity* FactoryStorage::
newEntity( const std::string& classname,const std::string& objname )
{
dgDEBUG(15) << "New <"<<classname<<">Entity <"<<objname<<">"<<std::endl;
EntityMap::iterator entPtr;
if(! existEntity(classname,entPtr) )
{
DG_THROW ExceptionFactory( ExceptionFactory::UNREFERED_OBJECT,
"Unknown entity."," (while calling new_entity <%s>)",
classname.c_str() );
}
return entPtr->second(objname);
}
bool FactoryStorage::
existEntity( const std::string& name, EntityMap::iterator& entPtr )
{
entPtr = entityMap .find( name );
return ( entPtr != entityMap.end() );
}
bool FactoryStorage::
existEntity( const std::string& name )
{
EntityMap::iterator entPtr;return existEntity( name,entPtr );
}
void FactoryStorage::
listEntities(std::vector <std::string>& outList)
{
for (EntityMap::iterator it = entityMap.begin();
it != entityMap.end(); it++) {
outList.push_back(it->first);
// This checks efficiently if a key exists in an STL map using the
// approach suggested by Scott Meyer's Effective STL (item 24).
bool
FactoryStorage::existEntity (const std::string& name) const
{
EntityMap::const_iterator lb = entityMap.lower_bound (name);
return lb != entityMap.end ()
&& !(entityMap.key_comp () (name, lb->first));
}
}
/* --------------------------------------------------------------------- */
/* --- REGISTERERS ----------------------------------------------------- */
/* --------------------------------------------------------------------- */
EntityRegisterer::
EntityRegisterer( const std::string& entityClassName,
FactoryStorage::EntityConstructor_ptr maker)
:entityName( entityClassName )
{
dgDEBUGIN(15);
g_factory.registerEntity(entityClassName,maker);
dgDEBUGOUT(15);
}
EntityRegisterer::
~EntityRegisterer( void )
{
dgDEBUGIN(15);
//FIXME: this should be removed at some point.
void
FactoryStorage::listEntities (std::vector<std::string>& outList) const
{
typedef std::pair<std::string, EntityConstructor_ptr> iter_t;
BOOST_FOREACH (const iter_t& entity, entityMap)
outList.push_back(entity.first);
}
g_factory.deregisterEntity(entityName);
void
FactoryStorage::commandLine(const std::string& cmdLine,
std::istringstream& cmdArgs,
std::ostream& os)
{
if (cmdLine == "help")
{
os << "factory ";
std::string cmd2;
cmdArgs >> cmd2;
if (!cmdArgs.good ())
os
<< " <arg>\t\t\t\taccess to the factory (help <arg> for more detail)"
<< std::endl;
else if (cmd2 == "list")
os << "list\t\t:List all available entities." << std::endl;
else if (cmd2 == "listEntities")
os <<"listEntities\t:List available entities." << std::endl;
}
else if (cmdLine == "list")
commandLine("listEntities",cmdArgs,os);
else if (cmdLine == "listEntities")
{
os <<" List of available entities:" << std::endl;
typedef std::pair<std::string, EntityConstructor_ptr> iter_t;
BOOST_FOREACH (const iter_t& entity, entityMap)
os << " - " << entity.first << std::endl;
}
return;
}
dgDEBUGOUT(15);
}
/* --------------------------------------------------------------------- */
/* --- COMMAND LINE ---------------------------------------------------- */
/* --------------------------------------------------------------------- */
void FactoryStorage::
commandLine( const std::string& cmdLine,std::istringstream& cmdArgs,
std::ostream& os )
{
if( cmdLine == "help" )
{
os<< "factory ";
string cmd2; cmdArgs >> cmd2;
if(! cmdArgs.good())
os << " <arg>\t\t\t\taccess to the factory (help <arg> for more detail)" <<endl;
else if( cmd2 == "list" )
os << "list\t\t:List all available entities." << endl;
else if( cmd2 == "listEntities" )
os <<"listEntities\t:List available entities." << endl;
}
else if( cmdLine == "list" )
{
commandLine("listEntities",cmdArgs,os);
}
else if( cmdLine == "listEntities" )
{
os <<" List of available entities:" << endl;
for( EntityMap::iterator iter = entityMap.begin();iter!=entityMap.end();++iter )
{ os << " - " << iter->first << endl; }
}
return;
}
namespace dynamicgraph {
//! The global g_factory object.
FactoryStorage g_factory;
}
EntityRegisterer::EntityRegisterer
(const std::string& entityClassName, FactoryStorage::EntityConstructor_ptr maker)
: entityName (entityClassName)
{
dgDEBUGIN (15);
g_factory.registerEntity (entityClassName, maker);
dgDEBUGOUT (15);
}
EntityRegisterer::~EntityRegisterer()
{
dgDEBUGIN(15);
g_factory.deregisterEntity (entityName);
dgDEBUGOUT (15);
}
// The global factory.
FactoryStorage g_factory;
} // end of namespace dynamicgraph.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment