2

I have a template class Foo<T> that I derive in FooDerived<T>, which is in turn the template argument of a template class Bar<T>, i.e. I end up with something like Bar< FooDerived<T> >.

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived : public Foo<T>
{
    public:
        double compute() { return 42.0; }
};

template <typename T>
class Bar : public T
{
    public:
        void f()
        {
            // This function could vary depending on U (and possibly T)
            std::cout << "Bar< T<U> > : " << this->compute() << std::endl;
        }
};

I am trying to specialize Bar based on the template argument of FooDerived. For example, Bar< FooDerived<int> > and Bar< FooDerived<float> > will have a different behavior.

int main(void)
{
    Bar< FooDerived<int> > a;
    a.f();
    Bar< FooDerived<float> > b;
    b.f();
}

How can I achieve this efficiently in C++03 (or C++11)? By efficiently, I mean that I want to avoid useless code duplication (the real program involves a lot more types and functions than in this example). Also, people using the code should be able to add their specialization without needing to modify the code, so any kind of switch-like solution would not be adapted.

I have been looking at SFINAE-like solutions involving boost::mpl, e.g. boost::mpl::if_ with boost::is_same to check the type, but nothing seemed to achieve my objectives. I suppose that this may not be adapted for that kind of template specialization. I always end up with error: redefinition of 'Bar' since the compiler does not seem to see it as a specialization, for instance if I try to do something like this:

template class Bar : public T, private boost::mpl::if_, int, boost::mpl::false_>

Using the boost::mpl::if_, either as a private inheritance or an extra template argument, does not seem to make specialization possible.

So what is the proper way of achieving something like this?

UPDATE 1

Specializing all the cases is a possibility, but the real problem that hides behind this example is actually a lot more complicated. I have a CachedFunction<T> : public T where T derives from Function<U> (function, differentiable function, twice differentiable function etc.) where U is a storage type (dense or sparse matrices). CachedFunction contains a large number of functions whose implementation depends on the storage type and the type of function. Thus, metaprogramming seemed like a good way to avoid some code duplication.

UPDATE 2

In response to the answers provided: I am trying to avoid these explicit template specialization for all the cases involved. Try to imagine that there are 3 or 4 classes derived from Foo, that there are 2 or 3 types for Foo, and that Bar contains 6 or 7 functions that need to be processed differently based on the type of Foo and the derived class considered. Basically, for each i, j and k, I would need to implement:

template<> void Bar<FooDerived_i<Type_j> >::f_k(){ ... }

Thus, I am trying to see if there is any other "cleaner" way.

UPDATE 3

If I use boost::is_same, I can do something like this, but this solution makes it harder to handle a new type without modifying the code.

Here is an example:

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

typedef int type1;
typedef float type2;

template <typename T>
class Foo
{
    public:
        typedef T type_t;

        virtual double compute() { return 1.0; }
};

template <typename T>
class FooDerived
{
    public:
        typedef T type_t;

        double compute() { return 42.0; }
};

template <class T>
class Bar : public T
{
public:
    void f()
    {
        // types have to be known...
        if (boost::is_same<typename T::type_t, type1>::value)
            std::cout << "Bar< T<type1> > : " << this->compute() << std::endl;
        else if (boost::is_same<typename T::type_t, type2>::value)
            std::cout << "Bar< T<type2> > : " << this->compute() << std::endl;
    }
};

int main(void)
{
    Bar< Foo<type1> > a;
    a.f();
    Bar< FooDerived<type2> > b;
    b.f();
}
BenC
  • 8,729
  • 3
  • 49
  • 68
  • Could you show the code that tries to specialize `Bar` together with the error message from the compiler? – arne Jun 28 '13 at 06:33
  • @arne: sure, even though it's completely wrong apparently. I will update the question in a minute. – BenC Jun 28 '13 at 06:34
  • 3
    Why can't you just use ordinary specialization (without any SFINAE tricks)? I.e. you define a `template <> class Bar>` and a `template <> class Bar>`? – jogojapan Jun 28 '13 at 06:37
  • @jogojapan: because the problem is actually a bit more complicated. Each class derived from `Foo` may have a different implementation for the functions, so that would mean writing a specialization for a large number of cases (`number of types like int/float * number of functions like name() * number of derived classes`), thus duplicating a lot of code. I would like to avoid that, if this is possible. This is why I thought metaprogramming may hold the answer. I'll edit the question to add this information. – BenC Jun 28 '13 at 06:48
  • I see. So you'd like the specialization to affect only selected member functions, correct? Any data members (or their types) as well? – jogojapan Jun 28 '13 at 06:50
  • Closely related (possibly duplicate?): [How to specialize only some members of a template class](http://stackoverflow.com/questions/4955609/how-to-specialize-only-some-members-of-a-template-class) – jogojapan Jun 28 '13 at 06:51
  • So, interestng, [this](http://coliru.stacked-crooked.com/view?id=06ac9c653468cfa145415cb2c90966ba-ad7854d9cfd7979d567ca413f0830b65) does not work. I suppose you have no choice but to use traits? – Antonio Jun 28 '13 at 06:57
  • @jogojapan: the question you linked is a bit different. They talk about a simple full template specialization, which is not what I am looking for, since I am looking for a solution that would allow me to avoid a lot of code duplication. – BenC Jun 28 '13 at 07:08
  • @BenC What do you mean by full template specialization? If you mean specializing the entire class template, then no, the question is about specializing _only some_ of its members. But if you mean _explicit_ (as opposed to _partial_) specialization, then yes, the question is about explicit specialization, but so is yours... (or, I think it is) – jogojapan Jun 28 '13 at 07:11
  • @jogojapan: yes sorry, explicit is the right word. But it deals with a "direct" explicit specialization for a simple case where writing all the explicit specialization for all the cases is a valid option. As the number of cases grows, there may be better solutions. – BenC Jun 28 '13 at 07:14

3 Answers3

0

(Note, this answers first version of the question If you think it should be deleted please let me know through comments)

You can do:

template <typename T>
class Bar : public T
{
    public:
        std::string name();
};

template<>
std::string Bar<FooDerived<int> >::name() {
  return std::string("int");
  }

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
  }

Tested here: http://coliru.stacked-crooked.com/view?id=9054d3356f438b31b1adbb595620d838-ad7854d9cfd7979d567ca413f0830b65

Thanks to jogojapan for valuable suggestion!

Antonio
  • 19,451
  • 13
  • 99
  • 197
0

How about this?

template<>
struct Bar< FooDerived<int> >
{
   // int spezialisation here
};

template<>
struct Bar< FooDerived<float> >
{
   // float spezialisation here
};

For only specialisiing a few function with different behaviour but no differnt signature there is the following way:

template<>
std::string Bar<FooDerived<float> >::name() {
  return std::string("float");
}

Update:

As a note to your second update, I think specialistation is the only clean way to implement different behaviour in this case. But you can of course reuse lot of code if you use templates in a more granular way:

template<class T>
std::string name_impl(T& t) {
  // generic implementationm
}

std::string name_impl(Bar<FooDerived<float>& t) {
  // implementation
}

template<class T>
std::string Bar<T>::name() {
  // something befor
  return name_impl(*this);
}

So you can switch to compile time polymorphism (with inheritance, enable_if, ...) via function overloading which might be easier than template specialistation. This is template pattern at compile time.

Edit: As a not to your third update I would suggest to read my answer here. With your implementation you will most likely get problems in your method f.

Community
  • 1
  • 1
Jan Herrmann
  • 2,717
  • 17
  • 21
  • @jogojapan OK I will change it. – Jan Herrmann Jun 28 '13 at 06:59
  • As I said in my question, full specialization of everything is a possibility, but I want to avoid duplicating a lot of code. This is the worst case solution that I would like to avoid if something else is doable. – BenC Jun 28 '13 at 07:06
  • Thanks I dindn't compile it. – Jan Herrmann Jun 28 '13 at 07:09
  • It's hard to make a simple example out of a problem which is a bit more complicated, hence the multiple updates to try to steer the answers toward the right direction. Basically, this concerns [this](https://github.com/roboptim/roboptim-core/blob/master/include/roboptim/core/filter/cached-function.hxx) class (`[...]Function` is a typedef to a `Generic[...]Function`). I am trying to add the sparse matrix support for these `CachedFunctions` while factorizing the code a bit. I'll investigate the solution your suggest and get back to you asap. Thanks! – BenC Jun 28 '13 at 08:16
0

As you said in one comment, specializing the class would involve much code duplication in a real-world example, so it is to be avoided.

If you want, as you state in the question, "specialize" with respect to the template parameter of Foo (or FooDerived, for that matter) you should probably consider to define a template template parameter in Bar, since otherwise you can definitely instantiate a Bar, for example, thus the template argument of Bar has no template parameters.

You can do this via

template <template<typename> class T, typename Arg> class Bar : public T<Arg> {
public:
    std::string name()
    {
        return "Bar<T<Arg>>";
    }
};

That said, the following code shows a method that can be used to obtain specialized behaviours without needing to specialize the whole class

  1 #include <string>
  2 #include <iostream>
  3 
  4 namespace ExampleSpecializations{
  5         template <typename T>
  6         std::string name(){
  7                 return "generic";
  8         }       
  9         template <>
 10         std::string name<int>(){
 11                 return "int";
 12         }
 13 }
 14 
 15 template <typename Arg>
 16 struct Bar {
 17         std::string name()
 18         {
 19                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<Arg>() + std::string(">");
 20                 return ret; 
 21         }       
 22 };      
 23 
 24 int main()
 25 {
 26         Bar<int> a;
 27         std::cout << a.name() << std::endl;
 28         Bar<char> b; 
 29         std::cout << b.name() << std::endl;
 30 }       
 31 

note that the use of external functions (from line 4 to line 13) also provide a good encapsulation, since you are not adding member functions which do not need access to anything internal to the involved classes.

  1 #include <string>
  2 #include <iostream>
  3 
  4 template <typename T>
  5 struct Foo1{
  6 };              
  7                         
  8 template <typename T>   
  9 struct Foo2{
 10 };              
 11 
 12 namespace ExampleSpecializations{
 13         template <typename T>
 14                 std::string name(const T& ){
 15                         return "generic"; 
 16                 }       
 17         template <>
 18                 std::string name(const int&){
 19                         return "int";
 20                 }       
 21         template <typename T>
 22                 std::string name(const Foo1<T>& ){
 23                         std::string  ret = "Foo1<" + ExampleSpecializations::name<T>(T()) + ">";
 24                         return ret;  
 25                 }       
 26         template <typename T>
 27                 std::string name(const Foo2<T>& ){
 28                         std::string  ret = "Foo2<" + ExampleSpecializations::name<T>(T()) + ">";
 29                         return ret;  
 30                 }       
 31 }               
 32 
 33 template <typename Arg>
 34 struct Bar {
 35         std::string name()
 36         {
 37                 std::string ret = std::string("Bar<") + ExampleSpecializations::name<>(Arg()) + std::string(">");
 38                 return ret; 
 39         }       
 40 };      
 41 
 42 int main()
 43 {
 44         Bar<int> a;
 45         std::cout << a.name() << std::endl;
 46         Bar<char> b; 
 47         std::cout << b.name() << std::endl;
 48         
 49         Foo1<int> f1i;
 50         std::cout << ExampleSpecializations::name(f1i) << std::endl;
 51         
 52         Foo2<int> f2i;
 53         std::cout << ExampleSpecializations::name(f2i) << std::endl;
 54         
 55         Bar<Foo2<int> > bf2i;
 56         std::cout << bf2i.name() << std::endl;
 57 }

try the code here Needless to say, I would suggest to make Bar::name a non-member function too.

Finally, if you don't want to provide a "generic" behaviour, you simply don't provide a default implementation, as in:

        template <typename T>
            std::string name(const T& );

by means of which, the previous code issues an error because the "name" method is invoked on Bar. The error is solved either commenting out line 47 or implementing the char specialization.

Stefano Falasca
  • 8,837
  • 2
  • 18
  • 24