5

I'm using the following compile-time 'trick' (based on ADL) to create a function that is only valid/defined/callable by classes in the same namespace.

    namespace Family1
    {
        struct ModelA{};
        struct ModelB{};

        template<typename T>
        bool is_in_Family1(T const& t) 
        {
            return true;
        }
    };

    namespace Family2
    {
        struct ModelC{};

        template<typename T>
        bool is_in_Family2(T const& t) 
        {
            return true;
        }
    };


    Family1::ModelA mA;
    Family2::ModelC mC;

    is_in_Family1(mA);          // VALID
    is_in_Family1(mC);          // ERROR

Now, I'd like to use this principle (or something similar) in order to produce a specialization of Foo::Bar (below) for classes belonging to each of the namespaces e.g. Family1.

    // I would like to specialize the method template Bar for classes in Family1 
    // namespace; and another specialization for classes in Family2 namespace
    struct Foo
    {
        template<typename T>
        void Bar( T& _T ){}
    };

For ease of maintenance and the large number of classes in each namespace, if possible, I'd like to perform this check without naming all the classes in a namespace.

Olumide
  • 5,397
  • 10
  • 55
  • 104
  • Why is `Foo` a template class who's constructor is also a template with a different type? `Foo myvar('4');`? Did you only mean to have one `template`? Because the outer one isn't used. I think this could be done if `Foo` were just a function, but I do not think it can be for a struct. Or at least, not nicely. – Mooing Duck Jan 25 '12 at 18:23

2 Answers2

1

Your "trick" has one big problem. Try calling is_in_Family1(make_pair(Family1::ModelA(), Family2::ModelC()) and you will see that return true, because ADL will look into both the namespaces of ModelA and ModelC (because of pair<ModelA, ModelC>).

Ignoring that problem, with using your functions it is straight forward.

template<typename T> struct int_ { typedef int type; };

struct Foo
{
    template<typename T, 
             typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0
    >
    void Bar( T& t ){}

    template<typename T, 
             typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0
    >
    void Bar( T& t ){}
};

That calls Bar depending on whether it is in family2 or family1.

struct Foo
{
    template<typename T, 
             typename int_<decltype(is_in_Family1(*(T*)0))>::type = 0
    >
    void Bar( T& t, long){}

    template<typename T,
             typename int_<decltype(is_in_Family2(*(T*)0))>::type = 0
    >
    void Bar( T& t, long){}

    template<typename T>
    void Bar( T& t, int) {}

    template<typename T>
    void Bar( T& t ) { return Bar(t, 0); }
};

That one has also a generic fallback. And your code had undefined behavior because you used a reserved name. Don't use _T.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Thanks @litb ! (I'm currently studying your answer.) Is there an alternative to having `is_in_Family` return a boolean? Then again, why is a pair a valid argument to `is_in_Family`? – Olumide Jan 27 '12 at 12:02
  • @olu dunno about alrernatives. it is a valid argument because you made it a template so it accepts anything. – Johannes Schaub - litb Jan 27 '12 at 18:00
0

The quickest way I found to do this is using Boost Type Traits' is_base_of<>

I tried to use inheritence with template specialization but that didn't work because inheritance is ignored when template specialization is used so you'd have to specialize for each model. The answer to Partial specialization for a parent of multiple classes explains the problem.

Using type traits works provided you make Family1::ModelA and Family::ModelB subclasses of Family1:Family1Type and Family2::ModelC a subclass of Family2::Family2Type :

#include <iostream>
#include <boost/type_traits/is_base_of.hpp>

namespace Family1{

    struct Family1Type{};

    struct ModelA :public Family1Type{};
    struct ModelB :public Family1Type{};

    template<typename T>
    bool is_in_Family1(const T& t){
        return boost::is_base_of<Family1::Family1Type,T>::value;
    }
};

namespace Family2{
    struct Family2Type{};

    struct ModelC :public Family2Type{};

    template<typename T>
    bool is_in_Family2(const T& t){
        return boost::is_base_of<Family2::Family2Type,T>::value;
    }

};

using namespace std;
int main(int argc, char *argv[]) {

    Family1::ModelA mA;
    Family2::ModelC mC;

    std::cout << "mA is in Family1?  " << is_in_Family1(mA) << std::endl;
    std::cout << "mC is in Family2?  " << is_in_Family2(mC) << std::endl;

    //std::cout << "mC is in Family1?  " << is_in_Family1(mC) << std::endl; //ERROR!
    //std::cout << "mA is in Family2?  " << is_in_Family2(mA) << std::endl; //ERROR!

    return 0;
}

This results in the following output:

mA is in Family1?  1
mC is in Family2?  1

I don't think there is a way to declare Foo and specialize Foo::Bar<> in another namespace according to Specialization of 'template<class _Tp> struct std::less' in different namespace

Community
  • 1
  • 1
Joel
  • 2,928
  • 2
  • 24
  • 34
  • Thanks @Joel. How about the specialization of `Foo` for each namespace? – Olumide Jan 25 '12 at 21:52
  • Scratch that. What I'd really like to do is to specialize a method of `Foo` for each namespace. I've edited my post accordingly. – Olumide Jan 26 '12 at 13:36
  • @Olumide I can't seem to figure out how to do it at the moment without declaring the Foo struct in each namespace. I can't seem to define Foo outside of a namespace where I implement a specialization of it. – Joel Jan 26 '12 at 14:57