There is a hierarchy of classes:
template<typename T>
class FeatureInterface1 {
public:
void f1( void ) { static_cast<T&>(*this)._f1(); }
}
class Feature1 : public FeatureInterface1<Feature1> {
/* Allow interface class to access private elements */
friend class FeatureInterface<Feature1>;
private:
void _f1(void) { /* Do something there */ }
}
template<typename T>
class FeatureInterface2 {
public:
void f2( void ) { static_cast<T&>(*this)._f2(); }
}
class Feature2 : public FeatureInterface2<Feature2> {
/* Allow interface class to access private elements */
friend class FeatureInterface<Feature2>;
private:
void _f2(void) { /* Do something there */ }
}
Then there is a variadic data class:
template<typename... FEATURES> class Device {};
template<typename FEATURE, typename... OTHERS>
class Device<FEATURE, OTHERS...> : public Device<OTHERS...> {
public:
/* Contructor */
Device(FEATURE feature, OTHERS... others)
: Device<OTHERS...>(others...),
m_feature( feature ) {
}
private:
FEATURE m_feature;
};
and finally the full featured object made at compile time:
Device<Feature1, Feature2> device;
The task is to design a get<>()
function which returns the pointer to particular object using it's interface. Sample usage:
FeatureInterface1<Feature1>* ptr_f = get<FeatureInterface1<Feature1>>(device);
In other words something like get<0>
, get<1>
... accessors of std::tuple
but interface-class-defined rather than index-defined.
My idea is to use std::enable_if
in connection with std::is_base_of
...
The inspiration was taken from https://eli.thegreenplace.net/2014/variadic-templates-in-c/
I would be very glad to whatever willing to help me. Thanks in advance!
This is quite simple actually with if constexpr
:
template<typename FEATURE, typename... OTHERS>
class Device<FEATURE, OTHERS...> : public Device<OTHERS...> {
public:
...
template <typename FEATURE_INTERFACE>
FEATURE_INTERFACE& get()
{
if constexpr (std::is_base_of_v<FEATURE_INTERFACE, FEATURE>)
return m_feature;
else
return Device<OTHERS...>::template get<FEATURE_INTERFACE>();
}
...
};
Note that you'll get a compilation error if the Device
doesn't support the requested interface. However, if you want a nullptr
instead it's not that hard either with an additional specialization for empty Device
:
template<>
class Device<> {
public:
template <typename FEATURE_INTERFACE>
FEATURE_INTERFACE* get()
{
return nullptr;
}
};
And then just change the main implementation to return a pointer:
template <typename FEATURE_INTERFACE>
FEATURE_INTERFACE* get()
{
if constexpr (std::is_base_of_v<FEATURE_INTERFACE, FEATURE>)
return &m_feature;
else
return Device<OTHERS...>::template get<FEATURE_INTERFACE>();
}
I used a member function instead of non-member because in my opinion the implementation is way simpler this way and I also personally don't like those non-member friend getters :). Also, as Red.Wave mentioned in comments, it's quite easy to make a non-member getter using the member:
template <typename FEATURE_INTERFACE, typename... FEATURES>
FEATURE_INTERFACE* get(Device<FEATURES...>& device)
{
return device.template get<FEATURE_INTERFACE>();
}
You might also want to add const-overloads to all these getters for completeness.