Olumide
Olumide

Reputation: 5819

Detecting siblings of a CRTP base class

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

Answers (2)

Stephen Nutt
Stephen Nutt

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

Jay
Jay

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

Related Questions