Prev: I keep running into long term c++ programmers who refuse to use exceptions
Next: const is an overrated concept that is a source of extra typing and maintenan
From: Paul Bibbings on 28 Mar 2010 09:35 I'm wondering if there is any mileage in the idea of using template parameter/arguments solely for the purpose of reducing the clutter of an interface. To give a contrived example, suppose that I am modelling a system that comprises a known collection of members of the system and that I am only interested in how that system appears from a particular point of view - for the sake of an example, let's use the Solar System and it's nine planets. Say I'm wanting to model how it appears from Earth. Because it is possible to consider such a viewpoint as a facet of the `system' itself, it may (or may not, but let's suppose so) seem appropriate to avoid a first-thought model that comprises a SolarSystem class that maintains a collection of nine descendents of class Planet. (In this example, we could calculate appearances from tables of data without the need for Planet objects with state, so we avoid them.) However, I might then end up with a cluttered interface such as: class SolarSystem { // ... public: double get_distance_to_Mercury() const; double get_distance_to_Venus() const; double get_distance_to ... // ... }; and other such `noise'. As a result I might then start to think of something like: class Mercury; // incomplete class Venus; // ... class SolarSystem { // ... public: template<typename Planet> double get_distance() const; // ... }; and then provide explicit specializations to do the individual look-ups and calculations: template<> double SolarSystem::get_distance<Mercury>() const { // complicated, specific calculation here for Mercury } The user would then write: double dist_to_Mars = solar_system::get_distance<Mars>(); The idea here is then to use what is effectively a set of deliberately incomplete types to select specializations of a method, merely to achieve a simplification of the interface; but where the template arguments do not represent classes of objects in the model and have no other function other than to permit this selection. Is this a technique that is used at all in `real' code? I can't say that I have necessarily come across it much, if ever, but it did seem to fall naturally out of something I've been working on; although, on the face of it, I can't help feeling that there is something unnatural about it. It doesn't reduce code in any significant way, as there are still eight explicit specializations to write, but the interface seems cleaner. (Here I am assuming that there is no commonality between the algorithms required for each specialization, as might very well be the case in this example.) Regards Paul Bibbings -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Hakusa on 28 Mar 2010 15:48 On Mar 28, 8:35 pm, Paul Bibbings <paul.bibbi...(a)gmail.com> wrote: > I'm wondering if there is any mileage in the idea of using template > parameter/arguments solely for the purpose of reducing the clutter of an > interface. To give a contrived example, suppose that I am modelling a > system that comprises a known collection of members of the system > and that I am only interested in how that system appears from a > particular point of view - for the sake of an example, let's use the > Solar System and it's nine planets. Say I'm wanting to model how it > appears from Earth. > class SolarSystem { > // ... > public: > double get_distance_to_Mercury() const; > double get_distance_to_Venus() const; > double get_distance_to ... > // ... > }; > > and other such `noise'. As a result I might then start to think of > something like: > > class Mercury; // incomplete > class Venus; > // ... > > class SolarSystem { > // ... > public: > template<typename Planet> > double get_distance() const; > // ... > }; > > and then provide explicit specializations to do the individual look-ups > and calculations: > > template<> > double SolarSystem::get_distance<Mercury>() const > { > // complicated, specific calculation here for Mercury > } > > The user would then write: > > double dist_to_Mars = solar_system::get_distance<Mars>(); What problem does this solution solve? SolarSystem system; double x; // Your original: x = system.get_distance_to_Mercury(); // Changes to: x = system.get_distance< Mercury >(); Is this really more convenient? It's not much easier to type, and requires much more work on your side, having to define a partial type as well as a template specialization for each one, and the user can now type the fallowing code: x = system.get_distance< int >(); But, seemingly any time an argument is of an integral type, known at compile time, templates can be used to make the arguments compile time. At the slight cost of readability since it's unusual. PS: There are no longer nine main planets in the solar system. Pluto was declared a dwarf planet, or something. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Jeffrey Schwab on 28 Mar 2010 15:51
On 3/28/10 8:35 PM, Paul Bibbings wrote: > I might then end up with a cluttered interface such as: > > class SolarSystem { > // ... > public: > double get_distance_to_Mercury() const; > double get_distance_to_Venus() const; > double get_distance_to ... > // ... > }; > > and other such `noise'. As a result I might then start to think of > something like: > class SolarSystem { > // ... > public: > template<typename Planet> > double get_distance() const; > // ... > }; > The user would then write: > > double dist_to_Mars = solar_system::get_distance<Mars>(); > Is this a technique that is used at all in `real' code? Yes, all the time. It is not just for providing clean interfaces to human programmers, but also to enable template metaprogramming. Suppose you have some other family of functions, e.g. gravity_from, that must call distance_to. If you've written the "cluttered" version, then you must write eight (screw Pluto) almost-identical gravity_from_planet .... functions. If instead you've written the metaprogrammable version, then you can just write a single gravity_from<Planet> template that calls distance_to<planet>, and let the compiler dispatch to the right instantiation automatically. If you have many such sets of functions, this kind of simple metaprogramming can avoid a lot of code duplication (thought it eventually does impact build time). One quick word of advice: Just write a single function template to support nice syntax in the client code, then dispatch to either to a class template or to hand-written functions overloaded on a tag type. The rules for function template specialization are not as flexible as those for class template specialization or function overloading. Here's a small example. I'm going to disregard your "first thought" of a class hierarchy, because I don't understand why you want it, and I'm going to use mass instead of distance, because I can hard-code the planets' masses, whereas the distances vary over time. namespace planet { enum planet_t { mercury , venus , earth , mars , jupiter , saturn , uranus , neptune }; typedef double mass_t; template<planet_t P> struct tag { }; /* Mass in Kg, according to WikiAnswers. */ inline mass_t mass(tag< mercury >) { return 0.33e24; } inline mass_t mass(tag< venus >) { return 4.87e24; } inline mass_t mass(tag< earth >) { return 5.98e24; } inline mass_t mass(tag< mars >) { return 0.65e24; } inline mass_t mass(tag< jupiter >) { return 1900e24; } inline mass_t mass(tag< saturn >) { return 570e24; } inline mass_t mass(tag< uranus >) { return 87e24; } inline mass_t mass(tag< neptune >) { return 100e24; } template<planet_t P> mass_t mass() { return mass(tag<P>( )); } } #include <iostream> /* Doesn't this read nicely? "Mass of planet earth." If you wanted, * you could even provide a set of suggested using-declarations in a * public using.hh header. */ int main() { using planet::mass; std::cout << mass<planet::earth>() << std::endl; } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |