June 18, 2007

  • Another C++ template programming goodie

    So you have just created a sweet little template function that will save the world from global warming. Quite an amazing feat! You have decided declare the function like so:

        template <typename T>
        void SaveTheWorld(T moreOzone) { … }

    The problem is that the function has to be called many times, with many different template parameter types for it to be effective, so you need to make sure it is as efficient as possible. Since some of the template parameter types might be large objects, passing by value may not be a good idea. You decide to redefine SaveTheWorld like so:

        template <typename T>
        void SaveTheWorld(const T& moreOzone) { … }

    It turns out that this is going to perform a lot better than the previous definition, but for primitive types like ints, and chars, passing by value turns out to be a little quicker, and our secret ozone object needs a lot of those little guys. Wouldn’t it be nice if primitives could use the top one, and class types defined by the program use the bottom one? Well with some creative use of C++ templates, it turns out we can. The following ideas were taken from the book “C++ Templates, The Complete Guide” by David Vandevoorde and Nicolai M. Josuttis. I recommend this read for anyone who wants a serious understanding of C++ Templates.

    The first thing we need is a template that takes as template parameters a boolean value and two types. It will contain a typedef called BestType, which will be of type T1 if the boolean is true, and T2 if the boolean is false. To do this we will also need to utilize partial specialization.

        // primary template
        template <bool B, typename T1, typename T2>
        class TypeChooser;

        // partial specialization for true
        template<typename T1, typename T2>
        class TypeChooser<true, T1, T2> {
        public:
           typedef T1 BestType;
        };

        // partial specialization for false
        template<typename T1, typename T2>
        class TypeChooser<false, T1, T2> {
        public:
           typedef T2 BestType;
        };

    With these definitions, TypeChooser<true, int, char>::BestType will be type int, and TypeChooser<false, int, char>::BestType will be type char.

    Now we need a class template that will tell us whether a type is a class or not. Here is the code for that:

        template <typename T>
        class IsClass {
        private:
           typedef char One;
           typedef struct { char a[2]; } Two;
           template<typename C> static One test(int (C::*pmt));
           template<typename C> static Two test(…);
        public:
           enum { Yes = sizeof(IsClass<T>::test<T>(0)) == 1 };
           enum { No = !Yes };
        };

    This class might be a little confusing, so I’ll explain it here. If you declare IsClass with an integer type like IsClass<int>, then IsClass<int>::Yes is going to be false. The reason is because the declaration test(int (int::*pmt)) would be illegal, so the compiler will choose the test(…) function when it comes across IsClass<int>::test<int>(0), and return Two, whose size is 2. However, test(int (MyClass::*pmt)) would be legal, with int (MyClass::*pmt) being of type pointer to member function of MyClass. The following demonstrates the usage of this class.

        if(IsClass<int>::No) {
           cout << “NOT A CLASS!” << endl;
        }
        if(IsClass<MyClass>::Yes) {
           cout << “WE HAVE A CLASS!” << endl;
        }

    Finally, we’ll create a template class called ParamTChooser who will choose the correct parameter type for our SaveTheWorld function.

        template <typename T>
        class ParamTChooser {
        public:
           typedef typename
                TypeChooser<IsClass<T>::No, T, const T&>::BestType Type;
        };

    So ParamTChooser<T>::Type will be of type T if T is a primitive, and of type const T& if T is not a primitive. Finally, we redeclare SaveTheWorld as follows:

        template <typename T>
        void SaveTheWorld(typename ParamTChooser<T>::Type moreOzone);

    Now when we do SaveTheWorld<int>(myInt), it will pass myInt by value, and when we do SaveTheWorld<SomeClass>(myClass), it will pass myClass by const reference.
       

Comments (8)

  • Adam you need to stop reading about C++ templates and start reading about global warming. The ozone hole is so 1980s.

    I think this is a very neat example that is slowly making me believe that templates in C++ can really do anything. This level of generic programming really opens up a lot of possibilities for that old passion of mine, code reusability. This is certainly more elegant than my C# solution of dynamic byte code generation, though I do still think that the template syntax is difficult to grasp, and could be done more elegantly yet.

    On a technical note, what is Type::*pmt in C++. I can’t figure out what that actually is other than it not being allowed for primitive types.

    You have 24 hours.

  • Type::*pmt is the syntax for a member function pointer. For regular functions, the syntax to declare a variable fpt of type pointer to a function that returns void, and takes parameter string:

    void(*fpt)(string)

    For a member function, just add the class type and scope resolution operator:

    void(Type::*fpt)(string)

    In the case above, i made a pointer to a member function that returns int, and takes no parameters. Even if the type sent to the template doesn’t have member function with this signature, it will still seem legal to the compiler instantiating the template, which is why it is chosen.

  • Okay, you seem to know crazy awesome stuff about templates, and I’m trying to figure something out and for the life of me, I can’t seem to do it. So maybe if I ask you, that might work?

    If I have a templated function, but for one specific type, it has to perform differently, is there a way to do that? I’ve been trying for what seems like ever to get this. I want everything except std::string to follow the pattern, but strings have to be treated differently. Any ideas?

    Sorry to post this to your blog, but your blog seems to be the only useful source of template information on the entire internet. Or, at least, given my googling skills…

    Anyway. Good job knowing more about templates than the internet. I’ll try to read back here in the nearish future to see if you responded (assuming you’re not going to email me :) )

    -x

  • Hi there x!

    What you are trying to do is a common thing in template programming. It is called Template Specialization. Instead of posting a long comment on it, here are a couple of links to get you started on the subject. It is pretty simple :)

    http://www.cprogramming.com/tutorial/template_specialization.html
    http://www.gotw.ca/gotw/049.htm

    Good luck on your template programming endeavors!

  • I figured it had to be, I wonder why I am so bad at google… Thanks for the links. :)
    -x

  • but … ain’t you not quoting the examples from  Moderns C++ Design, by Andrei Alexandrescu.
    http://www.amazon.com/dp/0201704315?tag=modecdesi-20&camp=14573&creative=327641&linkCode=as1&creativeASIN=0201704315&adid=1HTS9ARD92V38YM8A37V&amp;

    You wouldn’t be doing justice to his work unless you mention his name

  • I have never read Modern C++ Design by Andrei Alexandrescu, though I know of its existence. I am quoting the examples of David Vandevoorde and Nicolai M. Josuttis, who very well may have gotten their ideas from Andrei. I have given my source,  “C++ Templates, The Complete Guide” by David Vandevoorde and Nicolai M. Josuttis the appropriate citing in the article.

  • Hi Adam, Is it possible to use a factory for a templated class? I have a class that accepts floats as the template as well as classes and I’d like to instantidate the class based on a user selection.

    Thanks – a

Post a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *