0

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

5
Contributors
6
Replies
7
Views
6 Years
Discussion Span
Last Post by thekashyap
0

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?

3

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).

Edited by mike_2000_17: n/a

Votes + Comments
nice example.
Great examples!!
I'm reading his book now; great stuff. Great example here, too.
2

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.

Votes + Comments
liked it.
Another good post
0

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?

Edited by stereomatching: n/a

0

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.

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.