Commit 47368b6b authored by David Thompson's avatar David Thompson

Make bridges register themselves at run time.

This is similar to VTK's autoinit scheme except that
no definitions need to be passed at compile time.
It will not work with statically-compiled external
libraries implementing bridges (unlike VTK's autoinit,
which will work at the expense of complexity).

Registration relies on the the C++ specification's
initialization order:

+ static variables with const initializers,
+ static variables with static initializers (BRepModel::s_bridges),
+ static variables with either no initializer or
  dynamic initializers (autoinit objects).
parent bd5e2bd3
#include "smtk/cgm/Bridge.h"
#include "smtk/cgm/TDUUID.h"
#include "smtk/util/AutoInit.h"
#include "smtk/util/UUID.h"
#include "smtk/model/Cursor.h"
......@@ -601,3 +602,12 @@ void Bridge::colorPropFromIndex(
} // namespace cgm
} // namespace cgmsmtk
smtkImplementsModelingKernel(cgm,cgmsmtk::cgm::Bridge);
// Ensure that anything which links to this library
// results in the bridge being registered (unless this
// file is included in a static library, in which case
// you must manually add the line below to your
// executable's source).
smtkComponentInitMacro(smtk_cgm_bridge);
......@@ -41,6 +41,7 @@ namespace cgmsmtk {
class CGMSMTK_EXPORT Bridge : public smtk::model::Bridge
{
public:
smtkDeclareModelingKernel();
typedef smtk::shared_ptr<Bridge> Ptr;
typedef smtk::model::BridgedInfoBits BridgedInfoBits;
static BridgePtr create();
......
......@@ -6,6 +6,13 @@
#include "smtk/model/ModelEntity.h"
#include "smtk/model/Storage.h"
#include "smtk/util/AutoInit.h"
#include <stdlib.h> // for atexit()
// Force the native (default) bridge to be registered
smtkComponentInitMacro(smtk_native_bridge);
namespace smtk {
namespace model {
......@@ -13,6 +20,9 @@ using smtk::util::UUID;
using smtk::util::UUIDs;
using smtk::util::UUIDArray;
/// A map of all the bridges which may be instantiated (indexed by name).
BridgeConstructors* BRepModel::s_bridges = NULL;
/**\brief Construction requires a container for storage.
*
* Storage is kept separate so that it can easily be serialized and deserialized.
......@@ -1213,6 +1223,38 @@ std::string BRepModel::shortUUIDName(const smtk::util::UUID& uid, BitFlags entit
return name;
}
void BRepModel::cleanupBridges()
{
delete BRepModel::s_bridges;
}
bool BRepModel::registerBridge(const std::string& bname, BridgeConstructor bctor)
{
if (!BRepModel::s_bridges)
{
BRepModel::s_bridges = new BridgeConstructors;
atexit(cleanupBridges);
}
if (!bname.empty() && bctor)
{
(*BRepModel::s_bridges)[bname] = bctor;
return true;
}
else if (!bname.empty())
{ // unregister the bridge of the given name.
BRepModel::s_bridges->erase(bname);
}
return false;
}
StringList BRepModel::bridges()
{
StringList result;
for (BridgeConstructors::const_iterator it = s_bridges->begin(); it != s_bridges->end(); ++it)
result.push_back(it->first);
return result;
}
IntegerList& BRepModel::entityCounts(
const smtk::util::UUID& modelId, BitFlags entityFlags)
{
......
......@@ -9,6 +9,7 @@
#include "smtk/SMTKCoreExports.h" // For SMTKCORE_EXPORT macro.
#include "smtk/SharedPtr.h"
#include "smtk/PublicPointerDefs.h"
#include "smtk/model/Bridge.h"
#include "smtk/model/Entity.h"
#include "smtk/model/FloatData.h"
#include "smtk/model/IntegerData.h"
......@@ -142,6 +143,9 @@ public:
std::string assignDefaultName(const smtk::util::UUID& uid);
static std::string shortUUIDName(const smtk::util::UUID& uid, BitFlags entityFlags);
static bool registerBridge(const std::string& bname, BridgeConstructor bctor);
static StringList bridges();
protected:
shared_ptr<UUIDsToEntities> m_topology;
smtk::shared_ptr<UUIDsToFloatData> m_floatData;
......@@ -151,10 +155,13 @@ protected:
smtk::shared_ptr<Bridge> m_defaultBridge;
smtk::util::UUIDGenerator m_uuidGenerator;
int m_modelCount;
static BridgeConstructors* s_bridges;
std::string assignDefaultName(const smtk::util::UUID& uid, BitFlags entityFlags);
IntegerList& entityCounts(const smtk::util::UUID& modelId, BitFlags entityFlags);
void prepareForEntity(std::pair<smtk::util::UUID,Entity>& entry);
static void cleanupBridges();
};
} // model namespace
......
......@@ -3,6 +3,16 @@
namespace smtk {
namespace model {
/**\brief Return the name of the bridge type (i.e., the name of the modeling kernel).
*
* Subclasses override this method by using the smtkDeclareModelingKernel
* and smtkImplementsModelingKernel macros.
*/
std::string Bridge::name() const
{
return "invalid";
}
/**\brief Transcribe an entity from a foreign modeler into SMTK Storage.
*
* On input, the \a entity will not be valid but if transcription is
......
......@@ -19,6 +19,8 @@ class Bridge;
class Cursor;
class Operator;
typedef std::map<smtk::util::UUID,smtk::shared_ptr<Bridge> > UUIDsToBridges;
typedef smtk::shared_ptr<Bridge> (*BridgeConstructor)();
typedef std::map<std::string,BridgeConstructor> BridgeConstructors;
/**\brief Bit flags describing types of information bridged to Storage.
*
......@@ -54,6 +56,45 @@ enum BridgedInformation
/// Bit-vector combinations of BridgedInformation values for requesting information to transcribe.
typedef unsigned long BridgedInfoBits;
/*\brief Declare that a class implements a bridge to a solid modeling kernel.
*
* Invoke this macro inside every class definition inheriting smtk::model::Bridge.
* Both smtk/model/DefaultBridge.{h,cxx} and smtk/cgm/Bridge.{h,cxx} are examples.
* Note that you must invoke this macro in the global namespace!
*
* You must also use the smtkDeclareModelingKernel macro in your bridge's header.
*/
#define smtkImplementsModelingKernel(Comp, Cls) \
/* Adapt create() to return a base-class pointer */ \
static smtk::model::BridgePtr baseCreate() { \
return Cls ::create(); \
} \
/* Implement autoinit methods */ \
void smtk_##Comp##_bridge_AutoInit_Construct() { \
smtk::model::BRepModel::registerBridge( \
#Comp, /* Can't rely on bridgeName to be initialized yet */ \
baseCreate); \
} \
void smtk_##Comp##_bridge_AutoInit_Destruct() { \
smtk::model::BRepModel::registerBridge( \
Cls ::bridgeName, \
NULL); \
} \
/* Declare the component name */ \
std::string Cls ::bridgeName(#Comp)
/*\brief Boilerplate for classes that bridge to a solid modeling kernel.
*
* Invoke this macro inside every class definition inheriting smtk::model::Bridge.
* Both smtk/model/DefaultBridge.{h,cxx} and smtk/cgm/Bridge.{h,cxx} are examples.
* Note that you must invoke this macro in a public section of your class declaration!
*
* You must also use the smtkImplementsModelingKernel macro in your bridge's implementation.
*/
#define smtkDeclareModelingKernel() \
static std::string bridgeName; \
virtual std::string name() const { return bridgeName; }
/**\brief A base class for bridging modelers into SMTK.
*
* SMTK can act as a bridge between other (foreign) solid modelers
......@@ -86,6 +127,7 @@ class SMTKCORE_EXPORT Bridge : smtkEnableSharedPtr(Bridge)
{
public:
smtkTypeMacro(Bridge);
virtual std::string name() const;
int transcribe(const Cursor& entity, BridgedInfoBits flags, bool onlyDangling = true);
......
#include "smtk/model/DefaultBridge.h"
#include "smtk/util/AutoInit.h"
#include "smtk/model/BRepModel.h"
namespace smtk {
namespace model {
......@@ -13,3 +16,5 @@ BridgedInfoBits DefaultBridge::transcribeInternal(const Cursor& entity, BridgedI
} // namespace model
} // namespace smtk
smtkImplementsModelingKernel(native,smtk::model::DefaultBridge);
......@@ -16,6 +16,7 @@ public:
smtkTypeMacro(DefaultBridge);
smtkCreateMacro(Bridge);
smtkSharedFromThisMacro(Bridge);
smtkDeclareModelingKernel();
protected:
virtual BridgedInfoBits transcribeInternal(const Cursor& entity, BridgedInfoBits flags);
......
......@@ -22,6 +22,9 @@ add_test(unitStorage ${EXECUTABLE_OUTPUT_PATH}/unitStorage)
add_executable(unitOperator unitOperator.cxx)
target_link_libraries(unitOperator SMTKCore SMTKCoreModelTesting)
if (SMTK_BUILD_CGM)
target_link_libraries(unitOperator cgmSMTK)
endif (SMTK_BUILD_CGM)
add_test(unitOperator ${EXECUTABLE_OUTPUT_PATH}/unitOperator)
add_executable(unitCursor unitCursor.cxx)
......
#include "smtk/util/AutoInit.h"
#include "smtk/model/Bridge.h"
#include "smtk/model/ModelEntity.h"
#include "smtk/model/Operator.h"
......@@ -8,6 +10,8 @@
#include "smtk/util/Testing/helpers.h"
#include "smtk/model/testing/helpers.h"
#include "smtk/options.h"
using namespace smtk::model;
template<typename T>
......@@ -64,6 +68,16 @@ int DidOperateWatcher(OperatorEventType event, const Operator& op, const Operato
return 0;
}
void testBridgeList(Storage::Ptr storage)
{
std::cout << "Default bridge is \"" << storage->bridgeForModel(smtk::util::UUID::null())->name() << "\"\n";
std::cout << "Available bridges\n";
StringList bridges = storage->bridges();
for (StringList::iterator it = bridges.begin(); it != bridges.end(); ++it)
std::cout << " " << *it << "\n";
std::cout << "\n";
}
class TestOutcomeOperator : public Operator
{
public:
......@@ -258,6 +272,7 @@ int main()
try {
testBridgeList(storage);
testOperatorOutcomes(storage);
testParameterChecks(storage);
testBridgeAssociation(storage);
......
......@@ -240,6 +240,9 @@
<suppress-warning text="skipping function 'smtk::model::SimpleModelSubphrases::shared_from_this', unmatched return type 'smtk::shared_ptr&lt;const smtk::model::SimpleModelSubphrases::SelfType&gt;'"/>
<suppress-warning text="skipping function 'smtk::model::ModelEntity::op', unmatched return type 'smtk::model::OperatorPtr'"/>
<suppress-warning text="skipping field 'BRepModel::s_bridges' with unmatched type 'std::map&lt;std::string,smtk::model::BridgeConstructor&gt;'"/>
<suppress-warning text="skipping function 'smtk::model::BRepModel::registerBridge', unmatched parameter type 'smtk::model::BridgeConstructor'"/>
@EXTRA_TYPESYSTEMS@
......
#ifndef __smtk_util_AutoInit_h
#define __smtk_util_AutoInit_h
// This file contains macros used to initialize components of SMTK
// that may defined in separate libraries but which need to be
// exposed to SMTK's core components.
//
// See smtk/model/Bridge.h and its subclasses for an example of how
// these macros are used to register components with BRepModel.
#define smtkAutoInitComponentMacro(C) \
void C##_AutoInit_Construct(); \
void C##_AutoInit_Destruct();
#define smtkAutoInitConstructMacro(C) \
C##_AutoInit_Construct();
#define smtkAutoInitDestructMacro(C) \
C##_AutoInit_Destruct();
/**\brief Register an SMTK component for use.
*
* Initialize the named SMTK component (such as a modeling kernel), ensuring
* it is correctly registered and unregistered. This call must be made in
* global scope in the translation unit of your executable (which can include
* code built into a shared library linked to your executable, but will not
* work as expected in code linked to your executable as part of a static
* library).
*
* @code{.cpp}
* #include "vtkAutoInit.h"
* smtkComponentInitMacro(smtk_cgm_bridge);
* @endcode
*
* If included in the global scope, the above snippet will ensure the
* global function smtk_cgm_bridge_AutoInit_Construct is called during
* dynamic C++ initialization and and the global function
* smtk_cgm_bridge_AutoInit_Destruct is called during finalization.
*/
#define smtkComponentInitMacro(C) \
smtkAutoInitComponentMacro(C) \
static struct C##_ComponentInit { \
/* Call <mod>_AutoInit_Construct during initialization. */ \
C##_ComponentInit() { smtkAutoInitConstructMacro(C) } \
/* Call <mod>_AutoInit_Destruct during finalization. */ \
~C##_ComponentInit() { smtkAutoInitDestructMacro(C) } \
} C##_ComponentInit_Instance;
#endif // __smtk_util_AutoInit_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment