diff --git a/CMakeLists.txt b/CMakeLists.txt index 28b724f9197d24f596c1407e22b9096bcdf09b32..131af57444895cdd68d6e693e866856b0dac2b8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ cmake_minimum_required( VERSION 2.8 ) # --------------------------------------------------------------------- project( Elastix ) +set( CMAKE_CXX_STANDARD 11 ) + # Place libraries and executables in the bin directory set( CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" diff --git a/Modules/Core/ComponentInterface/include/ComponentBase.h b/Modules/Core/ComponentInterface/include/ComponentBase.h new file mode 100644 index 0000000000000000000000000000000000000000..a84aef60d94f19902f13f0becf0ea9757c9bfac6 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/ComponentBase.h @@ -0,0 +1,18 @@ +#ifndef ComponentBase_h +#define ComponentBase_h + +#include <iostream> +#include <cstring> +namespace elx +{ + enum interfaceStatus { success, noaccepter, noprovider }; + + class ComponentBase { + public: + virtual interfaceStatus ConnectFrom(const char *, ComponentBase*) = 0; + virtual int ConnectFrom(ComponentBase*) = 0; + //protected: + virtual ~ComponentBase() {}; + }; +} // end namespace elx +#endif // #define ComponentBase_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Example3rdPartyCode.h b/Modules/Core/ComponentInterface/include/Example3rdPartyCode.h new file mode 100644 index 0000000000000000000000000000000000000000..6535a4f5bf916852fd5e530e606109b60b21690b --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Example3rdPartyCode.h @@ -0,0 +1,41 @@ +#ifndef Example3rdPartyCode_h +#define Example3rdPartyCode_h + +#include <iostream> +namespace Example3rdParty +{ + + // test case: there are two (slightly) incompatible codebases (i.e. 3rd party and 4th party!), each with an optimizer object and a metric object. + // goal: make elastix components of all objects and define a handshake that checks if connections can be made. + + /*************** below: example implementations of 3rd and 4th party code base (assume we cannot change that) *********************/ + + class Metric3rdPartyBase{ + public: + virtual int GetValue() = 0; + virtual int GetDerivative() = 0; + }; + + class Optimizer3rdPartyBase{ + public: + virtual int SetMetric(Metric3rdPartyBase*) = 0; + virtual int Optimize() = 0; + protected: + Metric3rdPartyBase* theMetric; + }; + + class SSDMetric3rdParty : public Metric3rdPartyBase { + public: + virtual int GetValue() { return 1; }; + virtual int GetDerivative() { return 2; }; + }; + + class GDOptimizer3rdParty : public Optimizer3rdPartyBase { + public: + GDOptimizer3rdParty(); + ~GDOptimizer3rdParty(); + virtual int SetMetric(Metric3rdPartyBase*); + virtual int Optimize(); + }; +} // end namespave Example3rdParty +#endif // #define Example3rdPartyCode_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Example4thPartyCode.h b/Modules/Core/ComponentInterface/include/Example4thPartyCode.h new file mode 100644 index 0000000000000000000000000000000000000000..d3701f96a80f546cbe099761fc61b55727274949 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Example4thPartyCode.h @@ -0,0 +1,40 @@ +#ifndef Example4thPartyCode_h +#define Example4thPartyCode_h + +#include <iostream> + +namespace Example4thParty +{ + +// test case: there are two (slightly) incompatible codebases (i.e. 3rd party and 4th party!), each with an optimizer object and a metric object. +// goal: make elastix components of all objects and define a handshake that checks if connections can be made. + +/*************** below: example implementations of 3rd and 4th party code base (assume we cannot change that) *********************/ + +class Metric4thPartyBase{ +public: + virtual int GetCost() = 0; // with different naming convention than 3rd party +}; + +class Optimizer4thPartyBase{ +public: + virtual int SetMetric(Metric4thPartyBase*) = 0; + virtual int DoOptimization() = 0; // with different naming convention than 3rd party +protected: + Metric4thPartyBase* theMetric; +}; + +class SSDMetric4thParty : public Metric4thPartyBase { +public: + virtual int GetCost() { return 3; }; +}; + +class GDOptimizer4thParty : public Optimizer4thPartyBase { +public: + GDOptimizer4thParty(); + ~GDOptimizer4thParty(); + virtual int SetMetric(Metric4thPartyBase*); + virtual int DoOptimization(); +}; +} // end namespave Example4thParty +#endif // #define Example4thPartyCode_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/GDOptimizer3rdPartyComponent.h b/Modules/Core/ComponentInterface/include/GDOptimizer3rdPartyComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..1d281e9b9fa7541e14721693433c8f307937578a --- /dev/null +++ b/Modules/Core/ComponentInterface/include/GDOptimizer3rdPartyComponent.h @@ -0,0 +1,30 @@ +#ifndef GDOptimizer3rdPartyComponent_h +#define GDOptimizer3rdPartyComponent_h + +#include "ComponentBase.h" +#include "Interfaces.hxx" +#include "Example3rdPartyCode.h" +#include "Metric3rdPartyWrapper.h" +#include <string.h> + +namespace elx +{ + + class GDOptimizer3rdPartyComponent : + public Implements< + Accepting< MetricValueInterface, MetricDerivativeInterface >, + Providing< OptimizerUpdateInterface> + > + { + public: + GDOptimizer3rdPartyComponent(); + virtual ~GDOptimizer3rdPartyComponent(); + Example3rdParty::GDOptimizer3rdParty* theImplementation; + Metric3rdPartyWrapper* MetricObject; + //virtual int ConnectFrom(const char *, ComponentBase*); + int Set(MetricValueInterface*); + int Set(MetricDerivativeInterface*); + int Update(); + }; +} //end namespace elx +#endif // #define GDOptimizer3rdPartyComponent_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/GDOptimizer4thPartyComponent.h b/Modules/Core/ComponentInterface/include/GDOptimizer4thPartyComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..eff1bb78cd856c4f9b6ae6088c2b5d555fb05077 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/GDOptimizer4thPartyComponent.h @@ -0,0 +1,30 @@ +#ifndef GDOptimizer4thPartyComponent_h +#define GDOptimizer4thPartyComponent_h + +#include "ComponentBase.h" +#include "Interfaces.hxx" +#include "Example4thPartyCode.h" +#include "Metric4thPartyWrapper.h" + +namespace elx +{ + // wrapping into components: + class GDOptimizer4thPartyComponent : + public Implements < + Accepting< MetricValueInterface >, + Providing < OptimizerUpdateInterface > + > + + { + public: + GDOptimizer4thPartyComponent(); + virtual ~GDOptimizer4thPartyComponent(); + Example4thParty::GDOptimizer4thParty* theImplementation; + Metric4thPartyWrapper* MetricObject; + //virtual int ConnectFrom(const char *, ComponentBase*); + int Set(MetricValueInterface*); + int Update(); + }; + +} //end namespace elx +#endif // #define GDOptimizer4thPartyComponent_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/InterfaceTraits.h b/Modules/Core/ComponentInterface/include/InterfaceTraits.h new file mode 100644 index 0000000000000000000000000000000000000000..5dd03ec37bccab68b6be2b1450cb6d588532d0e5 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/InterfaceTraits.h @@ -0,0 +1,69 @@ +#ifndef InterfaceTraits_h +#define InterfaceTraits_h + +#include "Interfaces.h" + +namespace elx +{ +// Traits to get printable interface name +// default implementation +template <typename T> +struct InterfaceName +{ + static const char* Get() + { + return typeid(T).name(); + } +}; + +// a specialization for each type of those you want to support +// and don't like the string returned by typeid +template <> +struct InterfaceName < MetricValueInterface > +{ + static const char* Get() + { + return "MetricValueInterface"; + } +}; +template <> +struct InterfaceName < MetricDerivativeInterface > +{ + static const char* Get() + { + return "MetricDerivativeInterface"; + } +}; +template <> +struct InterfaceName < OptimizerUpdateInterface > +{ + static const char* Get() + { + return "OptimizerUpdateInterface"; + } +}; + + +// partial specialization of InterfaceName +template<template<typename> class TT, typename T1> +struct InterfaceName < TT<T1> > { + static const char* Get() + { + return InterfaceName<T1>::Get(); + } +}; + + +template <typename T> +struct AcceptorInterfaceName +{ + static const char* Get() + { + return InterfaceName<T>::Get(); + } +}; + +} // end namespace elx + + +#endif // #define InterfaceTraits_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Interfaces.h b/Modules/Core/ComponentInterface/include/Interfaces.h new file mode 100644 index 0000000000000000000000000000000000000000..281d763d3ebbaf8608afb70fd02bdf98b0b9ce57 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Interfaces.h @@ -0,0 +1,78 @@ +#ifndef Interfaces_h +#define Interfaces_h + +#include "ComponentBase.h" +#include "InterfaceTraits.h" +#include <typeinfo> + +namespace elx +{ +// Define the providing interfaces abstractly +class MetricDerivativeInterface { + public: + virtual int GetDerivative() = 0; +}; + +class MetricValueInterface { + public: + virtual int GetValue() = 0; +}; + +class OptimizerUpdateInterface { +public: + virtual int Update() = 0; +}; + +// Define the accepting interfaces as templated by the providing interface + +template<class InterfaceT> +class InterfaceAcceptor { +public: + + // Set() is called by a succesfull Connect() + // The implementation of Set() must be provided by component developers. + virtual int Set(InterfaceT*) = 0; + + // Connect tries to connect this accepting interface with all interfaces of the provider component. + int Connect(ComponentBase*); + +private: + bool isSet; +}; + +template<typename ... RestInterfaces> +class Accepting +{ + public: + interfaceStatus ConnectFromImpl(const char *, ComponentBase*) { return interfaceStatus::noaccepter; }; //no interface called interfacename ; + int ConnectFromImpl(ComponentBase*) { return 0; }; //Empty RestInterfaces does 0 successful connects ; +}; + +template<typename FirstInterface, typename ... RestInterfaces> +class Accepting<FirstInterface, RestInterfaces... > : public InterfaceAcceptor<FirstInterface>, public Accepting< RestInterfaces ... > +{ +public: + interfaceStatus ConnectFromImpl(const char *, ComponentBase*); + int ConnectFromImpl(ComponentBase*); +}; + +template<typename... Interfaces> +class Providing : public Interfaces... +{ +}; + +template<typename AcceptingInterfaces, typename ProvidingInterfaces> +class Implements : public AcceptingInterfaces, public ProvidingInterfaces, public ComponentBase +{ + public: + virtual interfaceStatus ConnectFrom(const char *, ComponentBase*); + virtual int ConnectFrom(ComponentBase*); +}; + +} // end namespace elx + +#ifndef ITK_MANUAL_INSTANTIATION +#include "Interfaces.hxx" +#endif + +#endif // #define Interfaces_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Interfaces.hxx b/Modules/Core/ComponentInterface/include/Interfaces.hxx new file mode 100644 index 0000000000000000000000000000000000000000..cade1ce157abcb8b48165d0993ff80785a50d2dd --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Interfaces.hxx @@ -0,0 +1,71 @@ +#ifndef Interfaces_hxx +#define Interfaces_hxx + +#include "InterfaceTraits.h" +namespace elx +{ +template<class InterfaceT> +int InterfaceAcceptor<InterfaceT>::Connect(ComponentBase* providerComponent){ + + InterfaceT* providerInterface = dynamic_cast<InterfaceT*> (providerComponent); + if (!providerInterface) + { + std::cout << "providerComponent does not have required " << InterfaceName < InterfaceT >::Get() << std::endl; + return 0; + } + // connect value interfaces + this->Set(providerInterface); // due to the input argument being uniquely defined in the multiple inheritance tree, all versions of Set() are accessible at component level + return 1; +} + +template<typename AcceptingInterfaces, typename ProvidingInterfaces> +interfaceStatus Implements<AcceptingInterfaces, ProvidingInterfaces>::ConnectFrom(const char * interfacename, ComponentBase* other) +{ + return AcceptingInterfaces::ConnectFromImpl(interfacename, other); +} + +template<typename AcceptingInterfaces, typename ProvidingInterfaces> +int Implements<AcceptingInterfaces, ProvidingInterfaces>::ConnectFrom(ComponentBase* other) +{ + return AcceptingInterfaces::ConnectFromImpl(other); +} + +template<typename FirstInterface, typename ... RestInterfaces> +interfaceStatus Accepting<FirstInterface, RestInterfaces... >::ConnectFromImpl(const char * interfacename, ComponentBase* other) +{ + // does our component have an accepting interface called interfacename? + if (0 ==std::strcmp(InterfaceName<InterfaceAcceptor<FirstInterface>>::Get(), interfacename)) + { + // static_cast always succeeds since we know via the template arguments of the component which InterfaceAcceptors its base classes are. + InterfaceAcceptor<FirstInterface>* acceptIF = static_cast<InterfaceAcceptor<FirstInterface>*> (this); + + // See if the other component has the right interface and try to connect them + if (1 == acceptIF->Connect(other)) + { + //success. By terminating this function, we assume only one interface listens to interfacename and that one connection with the other component can be made by this name + return interfaceStatus::success; + } + else + { + // interfacename was found, but other component doesn't match + return interfaceStatus::noprovider; + } + } + return Accepting< RestInterfaces ... >::ConnectFromImpl(interfacename, other); +} + +template<typename FirstInterface, typename ... RestInterfaces> +int Accepting<FirstInterface, RestInterfaces... >::ConnectFromImpl(ComponentBase* other) +{ + // static_cast always succeeds since we know via the template arguments of the component which InterfaceAcceptors its base classes are. + InterfaceAcceptor<FirstInterface>* acceptIF = static_cast<InterfaceAcceptor<FirstInterface>*> (this); + + // See if the other component has the right interface and try to connect them + // count the number of successes + return acceptIF->Connect(other) + Accepting< RestInterfaces ... >::ConnectFromImpl(other); +} + +} // end namespace elx + + +#endif // #define Interfaces_hxx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Metric3rdPartyWrapper.h b/Modules/Core/ComponentInterface/include/Metric3rdPartyWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..6612b04e15862cf04e0e2e1fbd8ec8dbc5f2c44f --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Metric3rdPartyWrapper.h @@ -0,0 +1,20 @@ +#ifndef Metric3rdPartyWrapper_h +#define Metric3rdPartyWrapper_h + +#include "Example3rdPartyCode.h" +#include "Interfaces.hxx" +namespace elx +{ +// An Optimizer3rdParty expects that Metric3rdParty will be set as input. All accepted interfaces by the Optimizer3rdPartyCompoment will be delegated to the Metric3rdPartyWrapper object. + class Metric3rdPartyWrapper : public Example3rdParty::Metric3rdPartyBase { +public: + void SetMetricValueComponent(MetricValueInterface*); + void SetMetricDerivativeComponent(MetricDerivativeInterface*); + virtual int GetValue(); + virtual int GetDerivative(); +private: + MetricValueInterface* metricval; + MetricDerivativeInterface* metricderiv; +}; +} // end namespace elx +#endif // #define Metric3rdPartyWrapper_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/Metric4thPartyWrapper.h b/Modules/Core/ComponentInterface/include/Metric4thPartyWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..1a481281dec62a18df619ec497bb76c3fb406c0d --- /dev/null +++ b/Modules/Core/ComponentInterface/include/Metric4thPartyWrapper.h @@ -0,0 +1,17 @@ +#ifndef Metric4thPartyWrapper_h +#define Metric4thPartyWrapper_h + +#include "Example4thPartyCode.h" +#include "Interfaces.hxx" +namespace elx +{ +// An Optimizer4thParty expects that Metric4thParty will be set as input. All accepted interfaces by the Optimizer4thPartyCompoment will be delegated to the Metric4thPartyWrapper object. + class Metric4thPartyWrapper : public Example4thParty::Metric4thPartyBase { +public: + void SetMetricValueComponent(MetricValueInterface*); + virtual int GetCost(); +private: + MetricValueInterface* metricval; +}; +} // end namespace elx +#endif // #define Metric3rdPartyWrapper_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/SSDMetric3rdPartyComponent.h b/Modules/Core/ComponentInterface/include/SSDMetric3rdPartyComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..860398a559b4862de5c5a9bc923e669b652b8f04 --- /dev/null +++ b/Modules/Core/ComponentInterface/include/SSDMetric3rdPartyComponent.h @@ -0,0 +1,25 @@ +#ifndef SSDMetric3rdPartyComponent_h +#define SSDMetric3rdPartyComponent_h + +#include "ComponentBase.h" +#include "Interfaces.hxx" +#include "Example3rdPartyCode.h" + +namespace elx +{ + // SSDMetric3rdPartyComponent provides a value and a derivative + class SSDMetric3rdPartyComponent : + public Implements< + Accepting<>, + Providing< MetricDerivativeInterface, MetricValueInterface> + > + { + public: + SSDMetric3rdPartyComponent(); + virtual ~SSDMetric3rdPartyComponent(); + Example3rdParty::SSDMetric3rdParty* theImplementation; + int GetValue(); + int GetDerivative(); + }; +} //end namespace elx +#endif // #define SSDMetric3rdPartyComponent_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/include/SSDMetric4thPartyComponent.h b/Modules/Core/ComponentInterface/include/SSDMetric4thPartyComponent.h new file mode 100644 index 0000000000000000000000000000000000000000..c07ff0abce595d7f55e6dda9c8aaaed38d19237c --- /dev/null +++ b/Modules/Core/ComponentInterface/include/SSDMetric4thPartyComponent.h @@ -0,0 +1,24 @@ +#ifndef SSDMetric4thPartyComponent_h +#define SSDMetric4thPartyComponent_h + +#include "ComponentBase.h" +#include "Interfaces.hxx" +#include "Example4thPartyCode.h" + +namespace elx +{ + // SSDMetric4thPartyComponent provides only a value and not a derivative + class SSDMetric4thPartyComponent : + public Implements< + Accepting<>, + Providing< MetricValueInterface> + > + { + public: + SSDMetric4thPartyComponent(); + virtual ~SSDMetric4thPartyComponent(); + Example4thParty::SSDMetric4thParty* theImplementation; + int GetValue(); + }; +} //end namespace elx +#endif // #define SSDMetric4thPartyComponent_h \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/Example3rdPartyCode.cxx b/Modules/Core/ComponentInterface/src/Example3rdPartyCode.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ef3f9d660296fc1475fd3cac8a1b915e024c8e9b --- /dev/null +++ b/Modules/Core/ComponentInterface/src/Example3rdPartyCode.cxx @@ -0,0 +1,26 @@ +#include "Example3rdPartyCode.h" +namespace Example3rdParty +{ +GDOptimizer3rdParty::GDOptimizer3rdParty() +{ + this->theMetric = nullptr; +} +GDOptimizer3rdParty::~GDOptimizer3rdParty() +{ +} +int GDOptimizer3rdParty::SetMetric(Metric3rdPartyBase* metric) +{ + this->theMetric = metric; + return 0; +} +int GDOptimizer3rdParty::Optimize() +{ + if (this->theMetric != nullptr) + { + std::cout << "GDOptimizer3rdParty->Optimize():" << std::endl; + std::cout << " theMetric->GetValue():" << theMetric->GetValue() << std::endl; + std::cout << " theMetric->GetDerivative():" << theMetric->GetDerivative() << std::endl; + } + return 0; +} +} // end namespace Example3rdParty \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/Example4thPartyCode.cxx b/Modules/Core/ComponentInterface/src/Example4thPartyCode.cxx new file mode 100644 index 0000000000000000000000000000000000000000..56cb20048bee40700c3bcc3c19b535149fe4c313 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/Example4thPartyCode.cxx @@ -0,0 +1,25 @@ +#include "Example4thPartyCode.h" +namespace Example4thParty +{ +GDOptimizer4thParty::GDOptimizer4thParty() +{ + this->theMetric = nullptr; +} +GDOptimizer4thParty::~GDOptimizer4thParty() +{ +} +int GDOptimizer4thParty::SetMetric(Metric4thPartyBase* metric) +{ + this->theMetric = metric; + return 0; +} +int GDOptimizer4thParty::DoOptimization() +{ + if (this->theMetric != nullptr) + { + std::cout << "GDOptimizer4thParty->DoOptimization():" << std::endl; + std::cout << " theMetric->GetCost():" << theMetric->GetCost() << std::endl; + } + return 0; +} +} // end namespace Example4thParty \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/GDOptimizer3rdPartyComponent.cxx b/Modules/Core/ComponentInterface/src/GDOptimizer3rdPartyComponent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..3f0e0db01c83d6a4f24d67490df5a12f0a9a1555 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/GDOptimizer3rdPartyComponent.cxx @@ -0,0 +1,32 @@ +#include "GDOptimizer3rdPartyComponent.h" + +namespace elx +{ +GDOptimizer3rdPartyComponent::GDOptimizer3rdPartyComponent() +{ + this->theImplementation = new Example3rdParty::GDOptimizer3rdParty(); + this->MetricObject = new Metric3rdPartyWrapper(); +} +GDOptimizer3rdPartyComponent::~GDOptimizer3rdPartyComponent() +{ + delete this->theImplementation; + delete this->MetricObject; +} + + +int GDOptimizer3rdPartyComponent::Set(MetricValueInterface* component) +{ + this->MetricObject->SetMetricValueComponent(component); + return 0; +} +int GDOptimizer3rdPartyComponent::Set(MetricDerivativeInterface* component) +{ + this->MetricObject->SetMetricDerivativeComponent(component); + return 0; +} +int GDOptimizer3rdPartyComponent::Update() +{ + this->theImplementation->SetMetric(this->MetricObject); + return this->theImplementation->Optimize(); // 3rd party specific call +} +} //end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/GDOptimizer4thPartyComponent.cxx b/Modules/Core/ComponentInterface/src/GDOptimizer4thPartyComponent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..cd797a94d9c1bb8855885e94c823528639cfacf8 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/GDOptimizer4thPartyComponent.cxx @@ -0,0 +1,29 @@ +#include "GDOptimizer4thPartyComponent.h" + +namespace elx +{ + +GDOptimizer4thPartyComponent::GDOptimizer4thPartyComponent() +{ + this->theImplementation = new Example4thParty::GDOptimizer4thParty(); + this->MetricObject = new Metric4thPartyWrapper(); +} + +GDOptimizer4thPartyComponent::~GDOptimizer4thPartyComponent() +{ + delete this->theImplementation; + delete this->MetricObject; +} + +int GDOptimizer4thPartyComponent::Set(MetricValueInterface* component) +{ + this->MetricObject->SetMetricValueComponent(component); + return 0; +} + +int GDOptimizer4thPartyComponent::Update() +{ + this->theImplementation->SetMetric(this->MetricObject); + return this->theImplementation->DoOptimization(); // 4th party specific call +} +} //end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/Metric3rdPartyWrapper.cxx b/Modules/Core/ComponentInterface/src/Metric3rdPartyWrapper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..18feaa555add72667d1d1a8256156ed77ef46e96 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/Metric3rdPartyWrapper.cxx @@ -0,0 +1,24 @@ +#include "Metric3rdPartyWrapper.h" + +namespace elx +{ +void Metric3rdPartyWrapper::SetMetricValueComponent(MetricValueInterface* metricValueComponent) +{ + this->metricval = metricValueComponent; +} + +int Metric3rdPartyWrapper::GetValue() +{ + return this->metricval->GetValue(); +} + +void Metric3rdPartyWrapper::SetMetricDerivativeComponent(MetricDerivativeInterface* metricDerivativeComponent) +{ + this->metricderiv = metricDerivativeComponent; +} + +int Metric3rdPartyWrapper::GetDerivative() +{ + return this->metricderiv->GetDerivative(); +} +} // end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/Metric4thPartyWrapper.cxx b/Modules/Core/ComponentInterface/src/Metric4thPartyWrapper.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0bda72bbbad99aa4d6ae5ba3685f317d695dd414 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/Metric4thPartyWrapper.cxx @@ -0,0 +1,15 @@ +#include "Metric4thPartyWrapper.h" + +namespace elx +{ +void Metric4thPartyWrapper::SetMetricValueComponent(MetricValueInterface* metricValueComponent) +{ + this->metricval = metricValueComponent; +} + +int Metric4thPartyWrapper::GetCost() +{ + return this->metricval->GetValue(); +} + +} // end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/SSDMetric3rdPartyComponent.cxx b/Modules/Core/ComponentInterface/src/SSDMetric3rdPartyComponent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c4bc8260ebdd4aa0dba97b14859dfcfb837d0019 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/SSDMetric3rdPartyComponent.cxx @@ -0,0 +1,22 @@ +#include "SSDMetric3rdPartyComponent.h" +namespace elx +{ +SSDMetric3rdPartyComponent::SSDMetric3rdPartyComponent() +{ + this->theImplementation = new Example3rdParty::SSDMetric3rdParty(); +} +SSDMetric3rdPartyComponent::~SSDMetric3rdPartyComponent() +{ + delete this->theImplementation; +} + +int SSDMetric3rdPartyComponent::GetDerivative() +{ + return this->theImplementation->GetDerivative(); +}; + +int SSDMetric3rdPartyComponent::GetValue() +{ + return this->theImplementation->GetValue(); +}; +} //end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/SSDMetric4thPartyComponent.cxx b/Modules/Core/ComponentInterface/src/SSDMetric4thPartyComponent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..de00af4de98fdc8b30c74a72d30ee324b90de619 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/SSDMetric4thPartyComponent.cxx @@ -0,0 +1,18 @@ +#include "SSDMetric4thPartyComponent.h" +namespace elx +{ + +SSDMetric4thPartyComponent::SSDMetric4thPartyComponent() +{ + this->theImplementation = new Example4thParty::SSDMetric4thParty(); +} +SSDMetric4thPartyComponent::~SSDMetric4thPartyComponent() +{ + delete this->theImplementation; +} + +int SSDMetric4thPartyComponent::GetValue() +{ + return this->theImplementation->GetCost(); // translate method name +}; +} //end namespace elx \ No newline at end of file diff --git a/Modules/Core/ComponentInterface/src/componenthandshake.cxx b/Modules/Core/ComponentInterface/src/componenthandshake.cxx new file mode 100644 index 0000000000000000000000000000000000000000..87492b3421751b307d47297914f09912b6027097 --- /dev/null +++ b/Modules/Core/ComponentInterface/src/componenthandshake.cxx @@ -0,0 +1,147 @@ +#include <iostream> + +// test case: Assume there are two (slightly) incompatible codebases (i.e. 3rd party and 4th! party), each with an optimizer object and a metric object. The example classes of 3rd and 4th party code bases cannot be changed, but the wrapping is in our control. +// goal: make elastix components of all objects and define a handshake that checks if connections can be made. + +#include "SSDMetric3rdPartyComponent.h" +#include "GDOptimizer3rdPartyComponent.h" +#include "SSDMetric4thPartyComponent.h" +#include "GDOptimizer4thPartyComponent.h" + + + +using namespace elx; + int main() { + { + std::cout << InterfaceName<MetricValueInterface>::Get() << std::endl; + + std::cout << AcceptorInterfaceName<InterfaceAcceptor<MetricValueInterface> >::Get() << std::endl; + + std::cout << InterfaceName<InterfaceAcceptor<MetricValueInterface> >::Get() << std::endl; + std::cout << InterfaceName<InterfaceProvider<MetricValueInterface> >::Get() << std::endl; + + + } + { + /************ testing interface casts *********** + * expected: ok + */ + SSDMetric3rdPartyComponent* tempmetric3p = new SSDMetric3rdPartyComponent(); + ComponentBase* metric3p = tempmetric3p; + + MetricValueInterface* valIF = dynamic_cast<MetricValueInterface*> (metric3p); + std::cout << valIF->GetValue() << std::endl; + + MetricDerivativeInterface* derIF = dynamic_cast<MetricDerivativeInterface*> (metric3p); + std::cout << derIF->GetDerivative() << std::endl; + } + /************ Connect metric4p to optimizer4p *********** + * expected: ok + */ + { + SSDMetric4thPartyComponent* tempmetric4p = new SSDMetric4thPartyComponent(); + ComponentBase* metric4p = tempmetric4p; // type returned by our component factory + + GDOptimizer4thPartyComponent* tempOptimizer4p = new GDOptimizer4thPartyComponent(); + ComponentBase* optimizer4p = tempOptimizer4p; // type returned by our component factory + + interfaceStatus IFstatus = optimizer4p->ConnectFrom("MetricValueInterface", metric4p); + + InterfaceAcceptor<MetricValueInterface>* opValIF = dynamic_cast<InterfaceAcceptor<MetricValueInterface>*> (optimizer4p); + if (!opValIF) + { + std::cout << "optimizer4p has no OptimizerValueInterface" << std::endl; + } + + // connect value interfaces + opValIF->Connect(metric4p); + + OptimizerUpdateInterface* opUpdIF = dynamic_cast<OptimizerUpdateInterface*> (optimizer4p); + if (!opValIF) + { + std::cout << "optimizer4p has no OptimizerUpdateInterface" << std::endl; + } + + // Update the optimizer component + opUpdIF->Update(); + } + /************ Connect metric3p to optimizer4p *********** + * expected: ok + * optimizer4p will only use/have access to the GetValue interface of metric3p + */ + { + SSDMetric3rdPartyComponent* tempmetric3p = new SSDMetric3rdPartyComponent(); + ComponentBase* metric3p = tempmetric3p; // type returned by our component factory + + GDOptimizer4thPartyComponent* tempOptimizer4p = new GDOptimizer4thPartyComponent(); + ComponentBase* optimizer4p = tempOptimizer4p; // type returned by our component factory + + InterfaceAcceptor<MetricValueInterface>* opValIF = dynamic_cast<InterfaceAcceptor<MetricValueInterface>*> (optimizer4p); + if (!opValIF) + { + std::cout << "optimizer4p has no OptimizerValueInterface" << std::endl; + } + + // connect value interfaces + if (!opValIF->Connect(metric3p)) + { + std::cout << "metric3p cannot connect to optimizer4p by ValueInterface" << std::endl; + } + + OptimizerUpdateInterface* opUpdIF = dynamic_cast<OptimizerUpdateInterface*> (optimizer4p); + if (!opValIF) + { + std::cout << "optimizer4p has no OptimizerUpdateInterface" << std::endl; + } + + // Update the optimizer component + opUpdIF->Update(); + } + /************ Connect metric4p to optimizer3p *********** + * expected: fail + * optimizer3p needs a metric with GetDerivative which metric4p doesn't have + */ + { + SSDMetric4thPartyComponent* tempmetric4p = new SSDMetric4thPartyComponent(); + ComponentBase* metric4p = tempmetric4p; // type returned by our component factory + + GDOptimizer3rdPartyComponent* tempOptimizer3p = new GDOptimizer3rdPartyComponent(); + ComponentBase* optimizer3p = tempOptimizer3p; // type returned by our component factory + + InterfaceAcceptor<MetricValueInterface>* opValIF = dynamic_cast<InterfaceAcceptor<MetricValueInterface>*> (optimizer3p); + if (!opValIF) + { + std::cout << "optimizer3p has no OptimizerValueInterface" << std::endl; + } + + // connect value interfaces + if (!opValIF->Connect(metric4p)) + { + std::cout << "metric4p cannot connect to optimizer3p by ValueInterface" << std::endl; + } + + //opValIF->Set(tempmetric4p); + + InterfaceAcceptor<MetricDerivativeInterface>* opDerivIF = dynamic_cast<InterfaceAcceptor<MetricDerivativeInterface>*> (optimizer3p); + if (!opDerivIF) + { + std::cout << "optimizer3p has no OptimizerDerivativeInterface" << std::endl; + } + // connect derivative interfaces + if (!opDerivIF->Connect(metric4p)) + { + std::cout << "metric4p cannot connect to optimizer3p by DerivativeInterface" << std::endl; + } + + + OptimizerUpdateInterface* opUpdIF = dynamic_cast<OptimizerUpdateInterface*> (optimizer3p); + if (!opValIF) + { + std::cout << "optimizer3p has no OptimizerUpdateInterface" << std::endl; + } + + // Update the optimizer component + // opUpdIF->Update(); // will fail since the metric does'nt have GetDerivative() + } + return 0; + } diff --git a/Modules/Core/elxModuleCore.cmake b/Modules/Core/elxModuleCore.cmake index 22b0e39670593db36c4d6cd1553bf233a05625d4..8e5e3e2dfbc60ffb02fc194444791290f73f169a 100644 --- a/Modules/Core/elxModuleCore.cmake +++ b/Modules/Core/elxModuleCore.cmake @@ -5,6 +5,7 @@ set( ${MODULE}_INCLUDE_DIRS ${${MODULE}_SOURCE_DIR}/Common/include ${${MODULE}_SOURCE_DIR}/Blueprints/include ${${MODULE}_SOURCE_DIR}/Install/include + ${${MODULE}_SOURCE_DIR}/ComponentInterface/include ) # Export libraries @@ -15,8 +16,17 @@ set( ${MODULE}_LIBRARIES # Module source files set( ${MODULE}_SOURCE_FILES ${${MODULE}_SOURCE_DIR}/Blueprints/src/elxBlueprint.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/Example3rdPartyCode.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/Example4thPartyCode.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/GDOptimizer3rdPartyComponent.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/GDOptimizer4thPartyComponent.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/Metric3rdPartyWrapper.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/Metric4thPartyWrapper.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/SSDMetric3rdPartyComponent.cxx + ${${MODULE}_SOURCE_DIR}/ComponentInterface/src/SSDMetric4thPartyComponent.cxx ) + # Compile library add_library( ${MODULE} STATIC "${${MODULE}_SOURCE_FILES}" ) diff --git a/Testing/Unit/CMakeLists.txt b/Testing/Unit/CMakeLists.txt index 1c5e579fe9afcd8052fd6994699df0987da404cd..2186fe7715fd5279d4dcc6a14c2f4ae0c6d06086 100644 --- a/Testing/Unit/CMakeLists.txt +++ b/Testing/Unit/CMakeLists.txt @@ -7,6 +7,7 @@ set( ElastixUnitTestFilenames elxExampleUnitTest.cxx elxBlueprintTest.cxx elxComponentFactoryTest.cxx + elxComponentInterfaceTest.cxx ) diff --git a/Testing/Unit/elxComponentInterfaceTest.cxx b/Testing/Unit/elxComponentInterfaceTest.cxx new file mode 100644 index 0000000000000000000000000000000000000000..43e2b6ee798b8a496fd9fc5bbcee4674d18bd692 --- /dev/null +++ b/Testing/Unit/elxComponentInterfaceTest.cxx @@ -0,0 +1,116 @@ +#include "SSDMetric3rdPartyComponent.h" +#include "GDOptimizer3rdPartyComponent.h" +#include "SSDMetric4thPartyComponent.h" +#include "GDOptimizer4thPartyComponent.h" + +#include "gtest/gtest.h" + +namespace elx { + +class InterfaceTest : public ::testing::Test { +public: + + virtual void SetUp() { + metric3p = new SSDMetric3rdPartyComponent(); + optimizer3p = new GDOptimizer3rdPartyComponent(); + + metric4p = new SSDMetric4thPartyComponent(); + optimizer4p = new GDOptimizer4thPartyComponent(); + } + + virtual void TearDown() { + delete metric3p; + delete optimizer3p; + delete metric4p; + delete optimizer4p; + } + // types as if returned by our component factory + ComponentBase* metric3p; + ComponentBase* optimizer3p; + ComponentBase* metric4p; + ComponentBase* optimizer4p; + +}; + +TEST_F( InterfaceTest, InterfaceNameTraits ) +{ + EXPECT_STREQ(InterfaceName<MetricValueInterface>::Get(), "MetricValueInterface"); + EXPECT_STREQ(InterfaceName<InterfaceAcceptor<MetricValueInterface> >::Get(), "MetricValueInterface"); +} + +TEST_F( InterfaceTest, DynamicCast ) +{ + int returnval; + //metric3p should have a MetricValueInterface + MetricValueInterface* valueIF = dynamic_cast<MetricValueInterface*> (metric3p); + ASSERT_NE(valueIF, nullptr); + EXPECT_NO_THROW(returnval = valueIF->GetValue()); + + //metric3p should have a MetricDerivativeInterface + MetricDerivativeInterface* derivativeIF = dynamic_cast<MetricDerivativeInterface*> (metric3p); + ASSERT_NE(derivativeIF, nullptr); + EXPECT_NO_THROW(returnval = derivativeIF->GetDerivative()); + + //optimizer3p should have a OptimizerUpdateInterface + OptimizerUpdateInterface* updateIF = dynamic_cast<OptimizerUpdateInterface*> (optimizer3p); + ASSERT_NE(updateIF, nullptr); + //EXPECT_NO_THROW(returnval = updateIF->Update()); // Update can only be called if metric and optimizer are connected + + //optimizer3p should have a InterfaceAcceptor<MetricValueInterface> + InterfaceAcceptor<MetricValueInterface>* valueAcceptorIF = dynamic_cast<InterfaceAcceptor<MetricValueInterface>*> (optimizer3p); + ASSERT_NE(valueAcceptorIF, nullptr); + + //optimizer3p should have a InterfaceAcceptor<MetricDerivativeInterface> + InterfaceAcceptor<MetricDerivativeInterface>* derivativeAcceptorIF = dynamic_cast<InterfaceAcceptor<MetricDerivativeInterface>*> (optimizer3p); + ASSERT_NE(derivativeAcceptorIF, nullptr); + +} + +TEST_F( InterfaceTest, ConnectByName ) +{ + interfaceStatus IFstatus; + EXPECT_NO_THROW(IFstatus = optimizer3p->ConnectFrom("MetricValueInterface", metric3p)); + EXPECT_EQ(IFstatus, interfaceStatus::success); + + EXPECT_NO_THROW(IFstatus = optimizer3p->ConnectFrom("MetricValueInterface", metric4p)); + EXPECT_EQ(IFstatus, interfaceStatus::success); + + EXPECT_NO_THROW(IFstatus = optimizer4p->ConnectFrom("MetricValueInterface", metric3p)); + EXPECT_EQ(IFstatus, interfaceStatus::success); + + EXPECT_NO_THROW(IFstatus = optimizer4p->ConnectFrom("MetricValueInterface", metric4p)); + EXPECT_EQ(IFstatus, interfaceStatus::success); + + + EXPECT_NO_THROW(IFstatus = optimizer3p->ConnectFrom("MetricDerivativeInterface", metric3p)); + EXPECT_EQ(IFstatus, interfaceStatus::success); + + EXPECT_NO_THROW(IFstatus = optimizer3p->ConnectFrom("MetricDerivativeInterface", metric4p)); + EXPECT_EQ(IFstatus, interfaceStatus::noprovider); + + EXPECT_NO_THROW(IFstatus = optimizer4p->ConnectFrom("MetricDerivativeInterface", metric3p)); + EXPECT_EQ(IFstatus, interfaceStatus::noaccepter); + +} + +TEST_F(InterfaceTest, ConnectAll) +{ + int connectionCount = 0; + EXPECT_NO_THROW(connectionCount = optimizer3p->ConnectFrom(metric3p)); + EXPECT_EQ(connectionCount, 2); // both MetricValueInterface and MetricDerivativeInterface are connected + + EXPECT_NO_THROW(connectionCount = optimizer3p->ConnectFrom(metric4p)); + EXPECT_EQ(connectionCount, 1); // only MetricValueInterface is connected + + EXPECT_NO_THROW(connectionCount = optimizer4p->ConnectFrom(metric3p)); + EXPECT_EQ(connectionCount, 1); // only MetricValueInterface is connected + + EXPECT_NO_THROW(connectionCount = optimizer4p->ConnectFrom(metric4p)); + EXPECT_EQ(connectionCount, 1); // only MetricValueInterface is connected + + + EXPECT_NO_THROW(connectionCount = metric4p->ConnectFrom(optimizer4p)); + EXPECT_EQ(connectionCount, 0); // cannot connect in this direction +} + +} // namespace elx