class template constraints

Hello,

how do I constraint a class template i.e. instead of

1
2
// T could be anything
template <typename T> class MipMap { ... };


do this

1
2
// T must derive from ("implement") (abstract) class ("interface") "Mipmapable"
template <Mipmapable T> class MipMap { ... };


This would help prevent me from doing static_casts which are consensually unwanted.
http://stackoverflow.com/questions/122316/template-constraints-c suggests that this is actually not possible. Does that still apply?
Using MSVC 2010 on Win7 x64.

Thaks for input~
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <type_traits>

struct Mipmaptable { virtual ~Mipmaptable() {} /* ... */ } ;

// is T derived from Mipmapable?
template < typename T > // http://en.cppreference.com/w/cpp/header/type_traits
struct is_mipmapable : std::conditional< std::is_base_of<Mipmaptable,T>::value,
                                         std::true_type, std::false_type >::type {} ;
// T could be anything
template < typename T, typename = void > class MipMap ; // not defined

// T must be derived from Mipmaptable
template < typename T > // http://en.cppreference.com/w/cpp/types/enable_if
class MipMap< T, typename std::enable_if< is_mipmapable<T>::value, void >::type >
{
   // ...
};

int main()
{
    struct A : Mipmaptable { /* ... */ };
    MipMap<A> mma ; // fine: A is derived from Mipmaptable
                      //  MipMap<A> is defined (specialization)

    MipMap<int> mmi ; // *** error: int is not derived from Mipmaptable
                      //  MipMap<int> is an incomplete type (generalization)

}
If there is.no useful functionality for other T, this can also be done with a static_assert in the body of MipMap (which has the benefit of more explicit compiler diagnostic.
Thanks so far. I needed to change
1
2
3
template < typename T > // http://en.cppreference.com/w/cpp/header/type_traits
struct is_mipmapable : std::conditional< std::is_base_of<Mipmaptable,T>::value,
                                         std::true_type, std::false_type >::type {} ;

to
1
2
3
template < typename T > // http://en.cppreference.com/w/cpp/header/type_traits
struct is_mipmapable : std::conditional< std::is_base_of<Mipmaptable<T>,T>::value,
                                         std::true_type, std::false_type >::type {} ;


to satisfy Mipmapable.h:
1
2
3
4
5
6
7
8
9
#pragma once

template <typename T>
class Mipmapable // A class deriving from this class can create MipMaps of class T
{
public:
	virtual ~Mipmapable() {}
	virtual T* makeMipMap();
};


leading to error:
1>main.obj : error LNK2001: unresolved external symbol "public: virtual class ByteMap * __thiscall Mipmapable<class ByteMap>::makeMipMap(void)" (?makeMipMap@?$Mipmapable@VByteMap@@@@UAEPAVByteMap@@XZ)
1>main.obj : error LNK2001: unresolved external symbol "public: virtual class ColorIndexMap * __thiscall Mipmapable<class ColorIndexMap>::makeMipMap(void)" (?makeMipMap@?$Mipmapable@VColorIndexMap@@@@UAEPAVColorIndexMap@@XZ)


For clarification, MipMap is sort of a dual paradigm in Computer Graphics terminology: It can basically mean a single image as well as multiple images belonging together. See https://en.wikipedia.org/wiki/Mipmap for nice self-explanatory image.

Files of interest:
ByteMap.h: http://pastebin.com/kbMedyS6
ColorIndexMap.h: http://pastebin.com/9uEK1WsM
MipMap.h: http://pastebin.com/G4aXJSYb

Thanks for the help!
> virtual T* makeMipMap();
If you don't plan to provide a body to that function, declare it pure virtual.
virtual T* makeMipMap() = 0;
Declare Mipmapable<T>::makeMipMap() as pure.
And override the function in the derived classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <type_traits>

template < typename T >
struct Mipmaptable
{
    virtual ~Mipmaptable() {}
    virtual T* makeMipMap() const = 0 ; // pure
    // ...
} ;

// is T derived from Mipmaptable<T>?
template < typename T > // http://en.cppreference.com/w/cpp/header/type_traits
struct is_mipmapable : std::conditional< std::is_base_of<Mipmaptable<T>,T>::value,
                                         std::true_type, std::false_type >::type {} ;
// T could be anything
template < typename T, typename = void > class MipMap ; // not defined

// T must be derived from Mipmaptable
template < typename T > // http://en.cppreference.com/w/cpp/types/enable_if
class MipMap< T, typename std::enable_if< is_mipmapable<T>::value, void >::type >
{
   // ...
};

// T could be anything
template < typename T, typename = void > class MipMap_Cubbi // Cubbi's idea
{
    // compile-time assertion: T must be derived from Mipmaptable<T>
    static_assert( is_mipmapable<T>::value, "T must be derived from Mipmaptable<T>" ) ;
};

// IIRC, VC++ 2010 does not support local types as template arguments
struct A : Mipmaptable<A> 
{
    // VC++ 2010 does not support the override specifier
    virtual A* makeMipMap() const /*override*/ { return new A ; }
    // ...
};

int main()
{

    MipMap<A> mma ; // fine: A is derived from Mipmaptable<A>
                    //  MipMap<A> is defined (specialization)

    MipMap_Cubbi<A> mmca ; // fine: A is derived from Mipmaptable<A>
                           // the compile-time assertion holds

    MipMap_Cubbi<int> mmci ; // error: static assertion failed:
                             // T must be derived from Mipmaptable<T>
}
Ok, here's one more important thing to learn: RAII (a concept that is central to C++).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <memory>

template < typename T >
struct Mipmaptable
{
    virtual ~Mipmaptable() {}

    // http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
    // http://msdn.microsoft.com/en-us/library/hh279674.aspx
    // http://en.cppreference.com/w/cpp/memory/unique_ptr
    virtual std::unique_ptr<T> makeMipMap() const = 0 ; // pure
    // ...
} ;

struct A : Mipmaptable<A>
{
    virtual std::unique_ptr<A> makeMipMap() const /*override*/
    { return std::unique_ptr<A>( new A ) ; }
    // ...
};
^ Good call. The ownership ambiguity was already annoying me while I was writing the code. There is so much std goodness yet to learn~
Topic archived. No new replies allowed.