Archived

This topic is now archived and is closed to further replies.

"Concepts" and Generic Programming

This topic is 5637 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Over and over agaon on these forums I see questions about how to organize bunches of objects and perform common operations on them. Most of the time, the answer given is to create an inheritance heirarchy and provide an interface class at the root (with a generic name like CObject or CCreatures - why do name warts persist anyway?). The reasoning is that these objects share common properties and thus can be connected in this way, which is quite sound. The question, therefore, is whether or not the use of concepts and generic programming can replace this (sometimes messy) use of inheritance and polymorphism, and whether it provides any practical gains in terms of code size and maintainability. Oh, but what are concepts some of you ask? A concept (in this particular context - generic programming, hereinafter GP) is an abstract specification of the requisite properties of a type in order for a generic algorithm to be applied to it or a generic data type be instantiated by it An example of a concept would be iterators in the Standard Template Library. The various iterator types require varying functionality (pre- and/or post-increment, access by dereference, etc) and embody different variations on the concept - refinements in this case because they place additional requirements on the base iterator concept. Obviously, a concept thus defines an abstract type - much like a (pure virtual?) base class. Unlike a base class, a concept isn''t necessarily expressed in code: a simple concept might be that the type provide an Update method (perhaps to be called while iterating through a sequential container). An immediate advantage of concepts over inheritance heirarchies is that the programmer does not have to locate her new class in the inheritance "family tree"; each instantiation of the concept can be completely independent (or may utilize inheritance as well). But what about virtual functions and the ability to fall back on that code? It means we only write the code once unless a particular class needs to override the base version. Well, in generic programming we only write the code once as well, and provide (partial) template specializations where we deem necessary. Unlike with virtual functions, however, there is no confusion when different ancestors in the inheritance tree override the base function; template specializations are unique. Another benefit of concepts and generic programming is the fact that a class can easily conform to multiple concepts (even retroactively) without upsetting any other functionality (so long as the concepts do not require, say, member functions with identical signatures). A convenient example is the .begin() and .end() methods provided by sequential STL container classes which allow the controlled sequence to be accessed via input and/or output iterators. To fit into multiple heirarchies, a class must engage in multiple inheritance which is often a troublesome topic. Is it then only ignorance of the power and availability of concepts and generic programming that keep them from proliferating all the more, or are there other issues involved? It is known that C++ is not well taught (both online and off), and Java, for one, as yet lacks generic programming facilities. Are these major contributing factors? Do you agree or disagree with my analyses? What are your opinions and experiences on/with concepts and generic programming?

Share this post


Link to post
Share on other sites
I was about to post a quick reply along the lines of "there are things you can''t solve with generic programming", but then I decided to try it out in my compiler first.... and found out I was wrong

Generic programming can be a very powerful tool to allow for "interfaces", without ANY underlying assumption aside from having a particularly named member function in the class around which the template is being instantiated - a problem I''ve run into more than once.

So I guess you''re partially right in that C++ isn''t really being taught to its full potential. I could have used this method of specialisation a lot!


People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
As far as concepts are concerned, the C++ Standard committee once considered the addition of signatures to the language. When a function declared a signature as the type of its parameter, the parameter's actual interface would have been checked for the presence of all the methods present in the signature's declaration, without need for the object to actually inherit from the signature. Thus no hierarchy would be involved. It was dropped.

(Note to Hackers: g++ 2.95 still has signatures as a deprecated feature. They're gone from g++ 3.0 ).

Good idea ? Bad idea ? C++ templates are somewhat more versatile though not as intuitive. One typical gripe is against the error reporting, when the failure occurs deep inside the STL. Boost now provides static concept-check facilities (dynamic behaviour is still the exclusive domain of OOP), but it is a tad late for the reputation of the STL.
The page-long error messages produced by parameterized classes did little to help either, especially when policy classes (e.g. Allocator, traits classes...) get involved. Things are getting better (VC7, g++3), but it's still not ideal.

And even if you stay within the boundaries of ANSI C++, templates are a relatively recent feature. Feature support was sketchy until recently, e.g. VC's issues with partial specialisation (no boost::lambda for you) or the broken STL implementations (Dinkumware).

The STL may have some tricky issues but they aren't overwhelming, (e.g. modifying a sequence while iterating), but books like Meyer's Effective STL help a lot. Problem is to find the right books, and to be willing to learn.

As long as there are people (e.g. here on gamedev) still caught using deprecated libraries (prestandard iostream), obsolete documentation (idem), or simply poorly-written books -- novices are a prime and hight vulnerable target -- you shouldn't be surprised that 'advanced' concepts' (read - anything somewhat counter-intuitive, or not covered in "The Complete Idiot's Guide to Beginner's C++ for Dummies in 24 hours" ) remain largely ignored (unless you're a software engineer, then you invent programming paradigms).

Now, if you are willing to give compile-time type safety up, you can move on to languages like ObjectiveC or SmallTalk, where call-by-name is the norm : regardless of the place of your type in the OO hierarchy, if it implements a method with a matching signature, the call is made; if not, you're in deep trouble. They essentially merge GP and OOP, which is why SmallTalk is still popular nowadays : in that respect it is more flexible than C++.

In my personal experience, people just go by the STL's reputation (lets face it, 'generic progamming' is nearly synonymous with 'C++ STL'). A C (as in 'cast-and-macro') programmer friend's arguments where that C++ templates, along with inheritance and overloading, could too easily be misused... no amount of arguing convinced him.

It stands to reason that once you know one language well enough, you are reluctant to go and learn another one, which doesn't make you immediately productive (case in point, my father uses PCSOFT's WinDev and just cannot find the time, nor the motivation, to learn any other language).

I gave a talk about the STL (though now I think I should have presented it in another way) to my work group; the reactions were mixed though negative overall. I also may actually give an exposé (and possibly a series of C++ lectures if I find the time and if there is some interest) to the incoming graduate class when fall comes.

To conclude, let me point out N. Josuttis upcoming book C++ Templates which should be a more approachable introduction to GP (along with Koenig & Moo's Accelerated C++) than Modern C++ Design.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]


[edited by - Fruny on June 25, 2002 6:17:11 AM]

Share this post


Link to post
Share on other sites
In my latest project, I''ve been almost exclusively using a generic programming standpoint. Unless a problem has specifically required some type of polymorphism (which could be solved using function pointers, but less cleanly), I''ve been using a templated approach. There are two things which don''t sit very well with me, however.

One is that I don''t actually have an in-code specification for certain generic types. For instance I might have a renderer implementation without a need for a base class. Without the need for some kind of abstract base class, there is no clean way to enforce a particular interface that I have seen, that the compiler can use. Instead, errors will manifest themselves when functions are asked to be called which do not exist in an implementation.

The second issue deals with compatibility. If I have a templated media system which takes a renderer and a windowing system as parameters, there are no clean facilities to check whether or not the chosen implementations of the renderer and windowing system are even compatible with one another. Instead, the compiler may flag errors which are indirectly related (when I ask for a clean facility, I basically mean a facility which directly checks your concepts, rather than falls back on indirect errors), or the compiler will not find any errors at all, but your program will crash at run-time.

Certainly you can avoid these two issues if you are careful in your design. But better explicit support to address these issues would certainly be nice.

"Don''t be afraid to dream, for out of such fragile things come miracles."

Share this post


Link to post
Share on other sites
Thank you for very well-reasoned responses.

MKV:
I''ve sometimes wondered whether it would be worth the while to write a free compiler that encourages/emphasizes Standard C++ (C++98) as a teaching tool to wean the masses off the old ways - with appropriate documentation, of course.

Fruny:
I''m not sure how I feel about signatures; I think I''d have voted against them had any asked me. I do think, though, that it would be useful to have a method of specifying concepts in code as Redleaf said. I remember an article in CUJ about parsing STL error messages (MSVC) - primarily stripping out/condensing the template type descriptions. Better debugging support would probably help with the acceptance of generic programming (correctly, as you said, represented to most by the STL), but since so few are aware of it or have tried it if aware, it''s probably not as impactful as I might imagine.

Redleaf:
Currently, the only way to "specify" concepts in code, and I use "specify" loosely, is selective use of template type names:

template < typename ConceptType > class SomeClass {...};

for a class that is to be instantiated by a ConceptType. ConceptType must, of course, be documented somewhere the programmer has acccess to - perhaps in comments?

There are some template metaprogramming tricks that can be used to ensure certain properties of the classes used to instantiate the template (for example ensuring that the class declaration exists in full), but they are limited in scope and fairly convoluted. It would be nice to have a language feature to completely specify the concept and ancillary co-relationships as well.

Oh, well...

Share this post


Link to post
Share on other sites
Well, remember writing some OO code for the first time?
Writing good templates is hard too. Perhaps harder, because it''s a less mature technique.

I just want to emphasize that templates are not a replacement for OO (nor vice-versa). They compliment eachother. Templates are for compile-time polymorphism, OO is for run-time polymorphism. You can emulate one using the other, but the results are horrendous in both cases.

quote:

One is that I don''t actually have an in-code specification for certain generic types.


Looks like the history is ''signature'' is the experimental (and deprecated) construct to model concepts in code. Perhaps it (or something like it) will be revisited in the near future with C++0x, the idea wasn''t really fleshed out until the BGL was developed, even though the stl makes extensive use of them.

The other problem with concepts, is how would you define them? One of the nice thing about concept-based generic programming, is that you only have to implement what is required. Adding signature/concept checking would defeat that, unless it everything in it was optional... which begs the question, why formally define it?

If you do want something in code now, you can make an template base class for the concept definition, and place a CompileTimeAssertion inside each concept''s methods. Inheriting from the impure base mean there''s no virtuosity overhead, but the base class method will be invoked unless you override them in the derived class. If a base class method is invoked, you''ll get a CompileTimeAssertion from the base template code, IFF that method is required.
(And if you''re a lazy bastard like me, you''d use a macro around the method names to automagically generate the code for the compile-time-assertions).
If you do try this, lemme know how well it works out.

quote:

To fit into multiple hierarchies, a class must engage in multiple-inheritance which is often a troublesome topic.


The only troublesome aspect of MI, is the ''dreaded-diamond shaped-inheritance''. This issue is entirely avoided by templates, because you "instance" a class using a template. Each ''template class instance'' is unique, ergo you never generate the dreaded diamond (or at least, it''s completely feasible to utterly avoid it). (Yes, if you place a static in a template, they don''t collide, each class definition get''s its own static property - it is exactly as though you defined two different classes).

quote:

Another benefit of concepts and generic programming is the fact that a class can easily conform to multiple concepts (even retroactively) without upsetting any other functionality


Yes, this a testament to their reusability. Also note that generic algorithms can adapt to existing non-generic legacy code, and are forward-compatible with new libraries. Again, I direct your attention to the BGL, and their property map, which allows you to choose the representation and interface to your graph data. Obviously using the provided representations is painless, and pre-built adaptors are provided for popular libraries such as LEDA. Supporting a new C or C++ based graph library is just a matter of writing the property map for it.

quote:

Is it then only ignorance of the power and availability of concepts and generic programming that keep them from proliferating all the more, or are there other issues involved?


Templates raise the abstraction bar above the heads of all but the largest corporations. The additional abstraction makes it that much more difficult to design & implement the library. It''s unattractive to a company to massively over-engineer their design & code, and then give away the source for anyone to use it.
Most template libraries are truly free - not some viral GPL "free".
I think the intrinsic openness of template libraries is a good thing, because it promotes peer-reviewed template libraries for everyone to use.

There’s a lot of template libraries already available, and as it’s the nature of the beast, they tend to focus on very broad (one could say generic) programming domains.

Share this post


Link to post
Share on other sites
quote:
Original post by MadKeithV
I was about to post a quick reply along the lines of "there are things you can''t solve with generic programming", but then I decided to try it out in my compiler first.... and found out I was wrong


With the advent of partial specialization, it was shown that C++ templates (alone) formed a turning complete language.

And you thought IOCCC C entries were hard to follow...

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor
With the advent of partial specialization, it was shown that C++ templates (alone) formed a turning complete language.

And you thought IOCCC C entries were hard to follow...



I really miss partial specialisation in Visual C++. Ironically, the very first major problem I ran into when trying to learn templates/generic programming was exactly that. I was writing template vector classes (the mathematical concept, not the container type), and wanted to "partially specialise" for 2D, 3D and 4D those operations which I knew to be performance critical.

And THEN I ran into the "not supported" section of the MSDN




People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
The most popular compiler in the world does not like templates. (VC++ 6), that''s why GP is not popular enough.

There are also some old programmers around which refuses to learn about GP and STL. Sadly, there are in a higher management position and makes decisions on the project rather than the younger STL capable programmer.

For usings GP and cepts to replace inheritance, I believe you have to use PTS (Partial template specialization). It''s messy and "popular" compiler don''t support it.

I find virtual functions wins.

For the case of the concept providing a "Update" method, you would not be able to store it in a STL container. You can''t have different templates types, without resorting to a base template class and PTS or the boost "any" library.

Which again defeats the purpose of trying to use GP as inheritance, no?

Share this post


Link to post
Share on other sites
quote:
Original post by Void
You can''t have different templates types, without resorting to a base template class and PTS or the boost "any" library.

Which again defeats the purpose of trying to use GP as inheritance, no?


As far as I see, it doesn''t - since in the case of inheritance, you would need to derive anyway. Using Generic Programming AND Inheritance/Polymorphism, you can create a base class that provides the basic "Update" method, purely virtual or any other way you like. Then using templated classes derived from the base class, you now have a class-unaware mechanism for "polymorphic" code calling.

Like the following:

  
class A
{
public:
void Update() { cout << "This is A"; }
};

class B
{
public:
void Update() { cout << "This is B"; }
};


class BaseGenericUpdateClass
{
public:
virtual void Update() = 0;
};

template< class T >
class GenericUpdateClass : public BaseClass
{
public:
GenericUpdateClass();
~GenericUpdateClass();
void Update() { m_t.Update(); }

private:
T m_t;
}

typedef GenericUpdateClass< ClassA > AUpdate;
typedef GenericUpdateClass< ClassB > BUpdate;

typedef stack<BaseGenericUpdateClass * > GenericUpdateContainer;

GenericUpdateContainer c;
AUpdate *a = new AUpdate();
BUpdate *b = new BUpdate();
c.push( a );
c.push( b );

while( !c.is_empty() )
{
BaseGenericUpdateClass *pUC = c.pop();
pUC->Update();
}


The advantage of the above code over "normal" inheritance-only polymorphism is that the container now contains implementations that have nothing do to with eachother, class-wise, but simply provide an update method. ANYTHING that provides the Update method can be wrapped in the GenericUpdateClass - and with some extra work you can even Update classes that have a differently-named Update function - using either partial specialisation of the GenericUpdateClass, or deriving another class from BaseGenericUpdateClass.

The downside is that compile-time checking will only throw an error if you actually try to instantiate a typedef that is invalid - for which there are workarounds, but you still have to be diligent enough to do them.

People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
I think that is exactly how boost::any container library works.

But it seems that if I want this sort of functionality, I could store a container of generalized functors(Modern C++ Design) or functional callbacks (boost).

The inheritance is misused, no?

Share this post


Link to post
Share on other sites
I''d say no, the inheritance isn''t misused, but I''m not very familiar with functors and how they can be used. It might well be that they are a more elegant solution to the same problem.

Could you explain?


People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
Partial specialization isn''t messy - the work-arounds for not having it are.

It''s not impossible to emulate partial specialization in MSVC - it just takes some work. I have a portion of the Loki library that compiles and runs correctly on MSVC7, and even some of it on MSVC6.

MSVC6 & 7 have an undocumented, non-standard feature that allow one to fully specialize nested template classes. For about 90% of the cases, this allows you to emulate partial specialization.
A similar trick can be done using a nested (and preferably anonymous) namespace and full specialization, and then it''s standard code.

...
A functor is just a class with an overloaded operator(). Generalized functors are more complicated - they allow you to create an object instance that contains all the information needed to invoke any invocable entity in C++. i.e. functions, methods, & functors (including other generalized functors).
They have full-value semantics, and allow you to treat C++ invocable entities as first-class citizens - like ML or LISP.

Share this post


Link to post
Share on other sites
quote:
Original post by MadKeithV
I''d say no, the inheritance isn''t misused, but I''m not very familiar with functors and how they can be used. It might well be that they are a more elegant solution to the same problem.

Could you explain?


Unfortunately, I haven''t digested generalized functor enough to be able to explain clearly. It''s still a topic I''m researching on.

In your case, we have added an additional interface BaseGenericUpdateClass and unnecessary inheritance. With generalized functors, we can avoid that.

Herb Stutter''s explains better at Guru of the Week.

http://www.gotw.ca/gotw/083.htm

Loki (Modern C++ Design) has a generalized functor library and so does boost::functional, worth checking out.

Share this post


Link to post
Share on other sites
I''ve just bounced around that GOTW article here at work ( a University Research Facility), and nobody here had ever heard of the explicit keyword.

Amazing how full-featured C++ is, I''m more thrilled by it every day. And indeed, these functors provide a cleaner way to do the thing I was trying to achieve in my code above - in about 5-10 lines of code a generic class can be written that does all and more that my class structure did.

Thanks for the pointers! This may make me a slightly better programmer.


People might not remember what you said, or what you did, but they will always remember how you made them feel.

Share this post


Link to post
Share on other sites
quote:
Original post by Void
In your case, we have added an additional interface BaseGenericUpdateClass and unnecessary inheritance. With generalized functors, we can avoid that.



Often you want to use both.

Suppose you wanted to implement undo/redo. You need a simple interface with two virtual methods, redo(...) & undo(...) (and a virtual dtor). You''d also need two stacks of interface pointers - and undo stack and a redo stack. Then you would implement that interface using a generalize functor.
With a couple of helper template functions (kinda like mem_fun), you don''t have to write the hundreds of classes otherwise required to implement undo/redo for every user action.

While the complexity of the C++ code involved is quite high, keep in mind that the task of implementing such a undo/redo system is not easy. The combination of inheritance of template code reduces the design & implementation problems to triviality.


Magmai Kai Holmlor

"Oh, like you''ve never written buggy code" - Lee

[Look for information | GDNet Start Here | GDNet Search Tool | GDNet FAQ | MSDN RTF[L] | SGI STL Docs | STFW | Asking Smart Questions ]

[Free C++ Libraries | Boost | ACE | Loki | MTL | Blitz++ | wxWindows| Spirit(xBNF)]

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor
Often you want to use both.

Suppose you wanted to implement undo/redo. You need a simple interface with two virtual methods, redo(...) & undo(...) (and a virtual dtor). You''d also need two stacks of interface pointers - and undo stack and a redo stack. Then you would implement that interface using a generalize functor.
With a couple of helper template functions (kinda like mem_fun), you don''t have to write the hundreds of classes otherwise required to implement undo/redo for every user action.



Loki''s functor has a bind feature for implementing redo/undo.
It uses Loki''s typelist, the equivalent of boost::tuple. Both are as confusing as another. But as I said, I haven''t digested this stuff fully.

Lambda looks cool but such limited compiler can benefit from it.

Share this post


Link to post
Share on other sites
Addendum: compile-time concept adherance checking.

I recently came across an excerpt of code from within STLPort or so which had a macro along the lines _STL__REQUIRES( InIt, InputIterator ) which checked that the template parameter InIt matched the concept InputIterator. I''m bandwidth constrained, so finding out more about that now is unpalatable. If anyone can shed any light on this, please do so. Thanks.

Share this post


Link to post
Share on other sites
in concept_check.h
  
#define _STLP_REQUIRES(__type_var, __concept) \
do { \
void (*__x)( __type_var ) = __concept##_concept_specification< __type_var >\
::##__concept##_requirement_violation; __x = __x; } while (0)


Which, for (VAR,CONCEPT) gives
  
do {
void (*__x)( VAR ) =
CONCEPT_concept_specification<VAR>::CONCEPT_requirement_violation;
__x = __x;
} while (0)


Where CONCEPT_requirement_specification is a template class containing one function, CONCEPT_requirement_violation. That function (directly or indirectly by instanciating other function templates) contains code executing the operations that the concept is supposed to implement.

e.g. at the end of the instanciation chain ''Default Constructible'' is checked by
  
template <class _Type>
static _Type
__default_constructor_requirement_violation(_Type) {
return _Type();
}

which, if executed, would default-construct its templated type.

This relies on the fact that templates that are not used are not instanciated. The assignment of the function pointer __x to itself , while a no-op, is sufficient to cause the instanciation of the template. Which will fail to compile if the template parameter does not implement the required operations.

Normally, the compiler will recognize that the function pointer is never really used during its lifetime and will optimize it away. If not, you have a performance cost equivalent to the initialisation of a single local parameter and its self assignment : one MOV to set it to a constant (the address) and one intra-register to itself (though _that_ should be optimized). The functions actually referenced are never executed !

STLPort use a macro to enables or disable concept checks (_STLP_USE_CONCEPT_CHECKS) depending on the compiler.

I think that sums it all. Pretty nifty IMHO.
Boost also has a concept-check module to that effect.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]

Share this post


Link to post
Share on other sites
quote:
Original post by MadKeithV
The advantage of the above code over "normal" inheritance-only polymorphism is that the container now contains implementations that have nothing do to with eachother, class-wise, but simply provide an update method. ANYTHING that provides the Update method can be wrapped in the GenericUpdateClass - and with some extra work you can even Update classes that have a differently-named Update function - using either partial specialisation of the GenericUpdateClass, or deriving another class from BaseGenericUpdateClass.


This discussion is very interesting, but I feel quite dumb... I understand how MadKeithV''s example works, but I don''t understand the advantages.

I feel that "ANYTHING that provides the Update method" could also inherit from the abstract base class. What''s the big deal with the templated approach? It seems a lot more messy to me.

I agree with Magmai Kai Holmor''s comments
quote:

I just want to emphasize that templates are not a replacement for OO (nor vice-versa). They compliment eachother. Templates are for compile-time polymorphism, OO is for run-time polymorphism. You can emulate one using the other, but the results are horrendous in both cases.


Virtual functions seems (IMHO) the best and cleanest solution to the "heterogeneous containers" problem.
quote:
By Oluseyi
[with GP,] the programmer does not have to locate her new class in the inheritance "family tree"


What do you mean? If I have access to CObject, I can derive from it wherever I want and create a new CObject.

Thanks for clearing this up for me. I guess what I would like is an explanation of those "practical gains in terms of code size and maintainability" that Oluseyi wrote about in his first post.

Cédric

Share this post


Link to post
Share on other sites
First, let me thank Fruny for that expose on _STL__REQUIRES and pointers to Boost''s concept check module. I''ll delve more deeply into both ASAP.

quote:
Original post by cedricl
I feel that "ANYTHING that provides the Update method" could also inherit from the abstract base class. What''s the big deal with the templated approach? It seems a lot more messy to me.

Runtime versus compile-time polymorphism. Many times we use virtual functions and inheritance merely to provide a consistent interface and call functions with impunity. However, in those instances we are required to pass a pointer to the object and rely on vtable replacement to locate the appropriate function. Consider: if we had three classes A, B and C and wished to write a function that operated on A, B and C, calling their respective Update methods (same signature) without using GP, we''d have a base class (say, X) and inherit the interface from X. Our function would then take a pointer to X and call ->Update() on that, which would work fine. It turns out in this case, however, that the object type is known at compile time, so that is redundant overhead and unnecessary inheritance.

Using GP, we merely have our function take the class type as a parameter (implict even) and call .Update() as if we knew the exact type. The complete code is generated at compile time, no problems. If we want different processing for different branches of the tree, we partially specialize the template funcion. Presto! Doing the same using inheritance might require RTTI and so forth.

quote:

I agree with Magmai Kai Holmor''s comments.

Absolutely. My original post stemmed from seeing people use, suggest and recommend inheritance for problems better solved by GP. There is no form of template manipulation that can replace dynamically selecting a renderer (eg DirectX or OGL) at runtime based on user input.

quote:

Virtual functions seems (IMHO) the best and cleanest solution to the "heterogeneous containers" problem.

It depends. If it''s merely interface inheritance for static usage, then I''m against it. If it''s any form of inheritance for runtime use, or any other form of runtime polymorphism, then it''s inheritance and virtual functions all the way.

quote:

What do you mean [by [with GP,] the programmer does not have to locate her new class in the inheritance "family tree"]? If I have access to CObject, I can derive from it wherever I want and create a new CObject.

Again, see above. The very name CObject seems to indicate overly broad inheritance schemes. using GP in conjunction with inheritance and polymorphism, the developer can achieve higher granularity and fewer design and implementation conflicts. Both tools are very useful.

quote:

I guess what I would like is an explanation of those "practical gains in terms of code size and maintainability" that Oluseyi wrote about in his first post.

Generally, generic algorithms are written type agnostic and with no assumptions as to internal data, emaning they''re usable with a wider variety of objects/types, provided the object/type conforms to the concept. That is why std::find works on all STL container iterators as well as raw pointers, and any other object that implements InputIterator semantics. This means writing less code for more objects, and only duplicating where specialization is needed.

Share this post


Link to post
Share on other sites
quote:
Original post by Oluseyi
Using GP, we merely have our function take the class type as a parameter (implict even) and call .Update() as if we knew the exact type. The complete code is generated at compile time, no problems. If we want different processing for different branches of the tree, we partially specialize the template funcion. Presto! Doing the same using inheritance might require RTTI and so forth.


Hmm... Function overloading is pretty much the equivalent of template specialization in the function world. You could specialize the same way you are doing with templates.

Otherwise, thanks for your post. It makes some sense, and I already use templates extensively to avoid virtual calls in critical situations, but I''m not sure that I see the complete potential of what is discussed here. I''ve got to think about it some more.

On another note, being able to remove ''virtuosity'', or being able to make a class truly final would allow the compiler to statically resolve the function calls at compile-time when the type is known, as was discussed in the General Programming forum a few weeks ago. Just a thought.

Cédric

Share this post


Link to post
Share on other sites
quote:
Original post by cedricl
Hmm... Function overloading is pretty much the equivalent of template specialization in the function world. You could specialize the same way you are doing with templates.



Yes. Templated functions are basically automatically-generated overloaded functions. It is nothing that you couldn''t do yourself, but with templates, you do not have to repeat the code in 20 (or more) different places, not make sure that you have an overload for each and every different type or combination of types (implicit conversions non-withstanding) that you may use in your program.

Generic Programming enables forward compatibility : STL algorithms will work even for types that were not considered when the library was designed.

Documents [ GDNet | MSDN | STL | OpenGL | Formats | RTFM | Asking Smart Questions ]
C++ Stuff [ MinGW | Loki | SDL | Boost. | STLport | FLTK | ACCU Recommended Books ]

Share this post


Link to post
Share on other sites