something like

class base
{
  public :
    virtual void draw() const = 0;
    virtual void area() const = 0;
};

class rectangle : public base
{
  public :
    virtual void draw() const {std::cout<<"rectangel\n";}
    virtual void area() const {std::cout<<"rect area\n";}
};

class triangle : public base
{
  public :
    void draw() const {std::cout<<"triangle\n";}
    virtual void area() const {std::cout<<"tri area\n";}
};

void drawShape(base const *basePtr)
{
  basePtr->draw();
  basePtr->area();
}

int main()
{  
  rectangle recta;
  triangle tri;

  base *basePtr[2] = {0};
  basePtr[0] = &recta;
  basePtr[1] = &tri;

  for(size_t i = 0; i != sizeof_array(basePtr); ++i) //the most important part
  drawShape(basePtr[i]);

  std::cout <<"system pause"<<std::endl;
  std::cin.get();
  return 0;
}

The most important part is I want to iterate different strategies of draw and
area by for loop one by one.Do I have any way to make template simulate the
behavior of line 37 and 38?

Thanks a lot

Recommended Answers

All 6 Replies

It might be possible but it would be a lot of work, and might not be worth it. Is this just for curiosity or a project or something else?

Just for curiosity

Yes, you can do so. But, of course, not using "dynamic polymorphism" but "static polymorphism" (as in, at compile-time). In your example, you don't need dynamic polymorphism, so it will work (when you really do need it, it won't).

The basis for achieving this is via a clever technique called "TypeList", developed the all-mighty template wizard himself: Andrei Alexandrescu. I suggest you read his book if you are really serious about learning to manipulate templates (his book: "Modern C++").

In a nutshell, here is the typelist technique. You need to build a list of types, but, in template meta-programming, everything has to be recursive (which is not really a problem since everything is done at compile-time, it will just increase compilation-time). So, you can build a single-linked-list of types with the following simple templates:

struct null_type {
  typedef null_type tail; //cycling terminator.
};

template <typename T, typename Tail = null_type>
struct type_list {
  typedef T type;
  typedef Tail tail;
};

So, the above can be traversed recursively (as with a normal linked-list) until the tail type is the null_type . Now, if you need random-access, as with any linked-list, to mimic it, you have to traverse from the beginning up to the element of the index (but since it is done a compile-time, it doesn't really matter at the end, no run-time overhead). Of course, the index has to be known at compile-time. So, here is a simple type-list indexing template:

//general type indexer
template <typename TypeList, unsigned int Index>
struct type_at {
  typedef typename type_at< typename TypeList::tail, Index - 1 >::type type;
};

//termination of the type indexing:
template <typename TypeList>
struct type_at<TypeList,0> {
  typedef typename TypeList::type type;
};

At this point, you could implement your example as:

template <typename DrawableShape>
void drawShape() {
  DrawableShape s;
  s.draw();
  s.area();
};

typedef type_list< rectangle, 
        type_list< triangle > > my_shape_types;

int main()
{  
  drawShape< type_at<my_shape_types, 0>::type >(); //this draws a rectangle.
  drawShape< type_at<my_shape_types, 1>::type >(); //this draws a triangle.

  std::cout <<"system pause"<<std::endl;
  std::cin.get();
  return 0;
}

Of course, the above does not have a for-loop, and it would be annoying if you had more than a trivially small amount of classes. But, of course, you can wrap this recursion with the drawShape function template, to make a drawAllShapes function template:

template <typename TypeList>
void drawAllShapes() {
  typename TypeList::type s;
  s.draw();
  s.area();
  drawAllShapes< TypeList::tail >(); //call on the tail.
};

template <>
void drawAllShapes<null_type>() { }; //don't forget the terminator!

//As before:
typedef type_list< rectangle, 
        type_list< triangle > > my_shape_types;

int main()
{  
  drawAllShapes< my_shape_types >(); //this draws all shapes in the list.

  std::cout <<"system pause"<<std::endl;
  std::cin.get();
  return 0;
}

If you absolutely want to see a "for" statement, you can also use a regular for-loop, but then, you will have to use a run-time index, which could lead to run-time overhead. To use a run-time for-loop on a compile-time type-list, you need another little trick:

template <typename TypeList>
void drawShape(unsigned int i) {
  if(i == 0) {
    typename TypeList::type s;
    s.draw();
    s.area();
  } else
    drawShape< typename TypeList::tail >(--i); //pseudo-recursive call.
};

//AGAIN: don't forget the terminator:
template <>
void drawShape<null_type>(unsigned int i) { };

//also, make a little template to count the number of types in a list:
template <typename TypeList, unsigned int Count = 0>
struct type_count {
  enum { value = type_count< typename TypeList::tail , Count + 1 >::value };
};

//again, terminator:
template <unsigned int Count>
struct type_count<null_type,Count> {
  enum { value = Count };
};

//As before:
typedef type_list< rectangle, 
        type_list< triangle > > my_shape_types;

int main()
{  
  for(unsigned int i = 0; i < type_count< my_shape_types >::value; ++i) 
    drawShape< my_shape_types >(i);
  
  std::cout <<"system pause"<<std::endl;
  std::cin.get();
  return 0;
}

Good chances are, the above pseudo-recursion in drawShape will be unrolled by the compiler (calls will be inlined and then collapsed into only the final draw() and area() function calls). But still, I wouldn't recommend the above unless you need to. The previous solution is preferable since everything is unrolled at compile-time, for sure.

I think that pretty much covers it (except for type factories). I'm sure you can find many more clever tricks and useful template meta-programming tricks in the Boost.MPL (they have pretty much implemented template meta-functions versions of all the functions in the C++ STL Algorithms library).

commented: I'm reading his book now; great stuff. Great example here, too. +6
commented: Great examples!! +14
commented: nice example. +4

Straddling compile-time techniques and run-time techniques requires a fair amount of care. The code tends to be fairly stylized.

For this paricular example:

#include <iostream>
#include <boost/any.hpp>
#include <vector>
#include <boost/mpl/list.hpp>
#include <boost/mpl/for_each.hpp>
using namespace boost ;

struct triangle
{
    void draw() const // polymorphic across many different types 
    { std::cout << "triangle {sides:" << a << ',' << b << ',' << c << "}\n" ; }
    int a, b, c ;
} ;

struct rectangle
{
    void draw() const
    { std::cout << "rectangle {widthxheight:" << width << 'x' << height << "}\n" ; }
    int width, height ;
} ;
struct circle
{
    void draw() const { std::cout << "circle {radius:" << radius << "}\n" ; }
    int radius ;
} ;

struct draw_it
{
    explicit draw_it( const any& a ) : object(a) {}

    template< typename T > void operator() (T) const
    {
        // the argument (which is ignored) is just a place holder; a null pointer to T
        try { any_cast<T>(object)->draw() ; throw drew_it() ; }
        catch( const bad_any_cast& ) {}
    }

    struct drew_it {} ;

    private:  const any& object ; // has a pointer to the actual object to be drawn
} ;

template< typename TYPELIST, typename ITERATOR >
void draw_all( ITERATOR begin, ITERATOR end )
{
    for( ; begin != end ; ++begin )
    {
        try
        {
            mpl::for_each<TYPELIST>( draw_it(*begin) ) ;
            std::cerr << "I don't know how to draw this object\n" ;
        }
        catch( const draw_it::drew_it& ) { /* ok */ }
    }
}

int main()
{
    std::vector<any> seq = { new circle{10}, new triangle{3,4,5},
                             new rectangle{50,80}, new int{90} } ;
    typedef mpl::list<rectangle*,triangle*,circle*> typelist ;
    draw_all<typelist>( seq.begin(), seq.end() ) ;
}

Note that mpl::for_each passes a value-initialized object for each type in the type sequence. This is fine for iteration over a type-sequence of simple types with value initialization. If a type in the type sequence turns out to one that cannot be value-initialized, mpl::for_each will fail at compile time. Even if these are value-initializable types, we won't be able to operate on objects with specific states.

Pointers are used in the above code snippet to work around this limitation.

commented: Another good post +13
commented: liked it. +4

Thanks a lot, but I haven't learn meta programming yet.
I will come back to read your code after I have the ability to do so

his book: "Modern C++"

Thanks for your recommendation
I am studying "c++ template complete guide"(ch15)
After I finish this book I would study about "Modern C++" and boost::mpl

By the way, do you know any gui library of c++ which design by the way of
generic programming rather than OO?I am not an expert of gui nor try to
design one by myself so I can't tell why there are a lot of gui libraries
are design by the way of oo but not generic programming?
Because oo fit the need of gui perfectly?

It might be possible but it would be a lot of work, and might not be worth it. Is this just for curiosity or a project or something else?

Can you give at least an outline how can you do this?

My understanding is that templates are a compiler feature, and to linker or "runner" it makes no difference whether code being executed was template-code or not.
In other words, its not possible to achieve dynamic-polymorphism with just templates.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.