Reputation: 5819
I am trying to use boost::is_base_of
in order to detect if a CRTP base class Generic
can identify its peers i.e. classes that T
is also derived from.
As shown in Generic<T>::init()
, I would like to use these mechanisms in order to allow the class Generic<T>
add pointers to functions one of its peers Bar1
or Bar2
(from which T
also derives) to a map. Unfortunately boost::is_base_of
is unable to detect classes, such as Bar3
, that T
does not derive from.
#include <iostream>
#include <cstdlib>
#include <string>
#include <typeinfo>
#include <map>
#include <boost/type_traits.hpp>
//////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class Bar
{
public:
void setValue()
{
std::cout << typeid(this).name() << std::endl;
}
};
class Bar1 : public Bar<char>{};
class Bar2 : public Bar<bool>{};
class Bar3 : public Bar<long>{};
//////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class Generic
{
public:
typedef void (T::*setter)();
void init();
};
template<typename T>
void Generic<T>::init()
{
std::map<std::string , Generic<T>::setter> setterMap;
if( boost::is_base_of<Bar1, T >::value ) setterMap["bar1"] = &Bar1::setValue;
if( boost::is_base_of<Bar2, T >::value ) setterMap["bar2"] = &Bar2::setValue;
if( boost::is_base_of<Bar3, T >::value ) setterMap["bar3"] = &Bar3::setValue;
std::cout << setterMap.size() << std::endl;
}
//////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class Foo : public Bar1 , public Bar2 , public Generic<Foo<T> >
{
public:
};
//////////////////////////////////////////////////////////////////////////////////////
int main()
{
Foo<int> f;
f.init();
return EXIT_SUCCESS;
}
//////////////////////////////////////////////////////////////////////////////////////
gcc error message:
In static member function ‘static void Generic<T>::init() [with T = Foo<int>]’:
error: cannot convert ‘void (Bar<long int>::*)()’ to ‘void (Foo<int>::*)()’ in assignment
Edit
To provide some context for this question. I'm trying to store pointers to the setValue
methods of the base classes of each Foo<T>
in a map for quick access. The choice of setValue
to call depends on a string, thus the map. Another class X
may inherit Bar1
and Bar3
but not Bar2
, and as before I would have to store pointers to the appropriate setValue
's for quick access. Generic<T>
aims to fulfil this role for Foo
, X
etc.
Upvotes: 1
Views: 299
Reputation: 3306
Jay is correct. I made the following changes and this appears to work.
template <bool, typename T> struct AddSetter;
template <typename T> struct AddSetter <true, T>
{
template<typename F>
void Set (std::map<std::string , typename Generic<T>::setter>& setterMap, const std::string& key, F fn)
{
setterMap[key] = fn;
}
};
template <typename T> struct AddSetter <false, T>
{
template<typename F>
void Set (std::map<std::string , typename Generic<T>::setter>& setterMap, const std::string& key, F fn)
{
}
};
template<typename T>
void Generic<T>::init()
{
std::map<std::string , Generic<T>::setter> setterMap;
AddSetter<boost::is_base_of<Bar1, T >::value, T>().Set (setterMap, "bar1", &Bar1::setValue);
AddSetter<boost::is_base_of<Bar2, T >::value, T>().Set (setterMap, "bar2", &Bar2::setValue);
AddSetter<boost::is_base_of<Bar3, T >::value, T>().Set (setterMap, "bar3", &Bar3::setValue);
std::cout << setterMap.size() << std::endl;
}
Upvotes: 1
Reputation: 3355
Off the top of my head I just can't envision this working without a middle man...
Is it possible to create a struct which encapsulates the desired logic with operator overloading? The struct would have a raw pointer and then you could deference it with an overload to ensure it was used correctly.
Another seemingly readable way is to create another forward facing class which acts as a Facade and then utilize that.
This is similar to Override contra-variance workaround needed
Upvotes: 1