GOPHERSPACE.DE - P H O X Y
gophering on raymii.org
This is a text-only version of the following page on https://raymii.org:
---
Title       :  Cooking with C++ templates and stronger types
Author      :  Remy van Elst
Date        :  13-06-2019
URL         :  https://raymii.org/s/blog/Cooking_with_Cpp_templates_and_stronger_types.html
Format      :  Markdown/HTML
---



To gain a better understanding of C++ templates I'm playing around
with them. Most of the online guides stop at the example of a simple template
to, for example, get the max of two inputs, or cover just a bit more (like how
to overload operators for your specific template classes to make `<<` and `+` / `-`
work). Combining templates with a stronger type to pass stuff around led me 
to a test kitchen. As in, some code with ingredients, amounts and an oven. 
One small thing kept it from working, after some feedback it turned out I was 
passing the wrong parameters to the template. Afterwards the error also made 
sense. This post covers both my learning and a small piece on stronger types.

 Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics.
 Please, if you found this content useful, consider a small donation using any of the options below:


 ://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go che
ck it out!

 Consider sponsoring me on Github. It means the world to
 me if you show your appreciation and you'll help pay the server costs.

 ode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60
 days. 

 




### Stronger typing

Would you rather have a class be clear in its intended usage or would you rather
look up the header/implementation and find out the details in a comment
lingering?

I'm all for the first, so next to playing around with templates, I tried to also
look into stronger typing. 

Lets say you have code that deals with `Amounts` as we do here, like `Liters`,
`Milliliters`, `Grams` or `Cubic Liters`. Or, units of measurement (distance), 
like `Kilometers`, `Miles`, `Klicks` or `AU's'` if you don't like volume.

One method could be, `pourWater(double water, int duration)`. Are you able to
tell if that's in  liters, milliliters, grams or maybe seconds? Probably your
documentation tells you that, but often there is just a comment lingering
somewhere, or you copy  example code used earlier.

If the method was `pourWater(Milliliters water, Seconds duration)` it would be
way more clear. I  still have more questions, like, how long, what pressure,
where does the water  exits the unit etc. But, this is for the sake of example.

The [Fluent C++][1] site has a library for this, `Named Types`. It has all kinds 
of advantages, like not having to overload standard operators like `<<`. 

There is another article there, [Getting the Benefits of Strong Typing in C++ 
at a Fraction of the Cost][2]. That's what were doing here, or at least, that
is what I tried to achieve. 

Here's my attempt to create these stronger classes:

    template 
    class Amount {
    public:
        T m_amount;

        Amount(T amount) : m_amount(amount) { }

        friend std::ostream &operator<<(std::ostream &out, const Amount &amount) {
            out << amount.m_amount;
            return out;
        }
    };

    template 
    class Grams : public Amount {
    public:
        Grams(T amount) : Amount(amount) {}
    };

    template 
    class Milliliters : public Amount {
    public:
        Milliliters(T amount) : Amount(amount) {}
    };


By using templates we also elliminate the need to specify the type we're able to
handle. It doesn't matter if I provide my `Grams` as a `double`, `int` or even
`long long`, all will work. You probably do need to make some partial template
specialization to get the correct behaviour, but that outside of the scope of 
this example. 

You could also still pass `Grams` to something that wants `Milliliters` if that
class accepts any `Amount` as its parameter. If you limit it to `Grams` it will
still accept `Milliliters` due to the inheretance.

If you're worried about overhead, the compiler will probably
optimize it all away to a basic type. And, if you're worried about overhead, 
why are you even looking at templates?

### The kitchen

Here's the example code I was cooking up. An ingredient has a name and an amount
and an amount has a unit. Instead of just passing the value as an `int` or
`double`,  I wanted to be able to pass the unit itself. For the example I've
used `Milliliters` and `Grams`, which adhere to a base class of `Amount`. In
hindsigt I'm not sure on the name of the base class, since `Unit` or
`Measurement` have also crossed my mind.

The `Ingredient` class takes a name and an `Amount`. The `Oven` class takes two
`Ingredients` and has a `Grill` method to create something delicious. As said 
in the above topic, by using specific classes to make the meaning of something 
more clear, you emit the need for comments.

### No matching constructor for initialization of Class

You can see the fixed code in the next section. The `Oven` template class:

    template 
    class Oven {
        public:
            Ingredient m_ingredient1;
            Ingredient m_ingredient2;
            Oven(Ingredient ingredient1, Ingredient ingredient2) :
            m_ingredient1(ingredient1),
            m_ingredient2(ingredient2)

I was calling the `Oven` with the following parameters:

    Ingredient> Milk {amount_milk, name_milk};
    Ingredient> Butter {amount_butter, name_butter};

    Oven>, Ingredient>> oven1 {Milk, Butter};

You might already see the problem, I did not however. I kept getting hit with:

    No matching constructor for initialization of
    'Oven >, Ingredient > >'

After trying different versions of the `Oven` class, different iterations of 
the method calling, I was stuck. You know that feeling when you're looking at 
the same problem for too long and can't figure it out? I was in that state.
Since templates are new to me I also wasn't sure what to search for anymore. 
In my mind, the `Oven` needed its `Ingredients`, which was why I passed them.

I posted my issue online and within 15 minutes received feedback. It turned out,
due to declaring it in the `Oven` constructor as `Ingredient`, I was already
specifying it to be an `Ingredient`, and the
`Oven>`  was redundant. Just
`Oven` was enough. With my code, I was giving the class an
`Ingredient>`.

By doing this, coding it up and trying to figure out what's wrong, I find myself
to get a better understanding of the thing I'm learning as to when I just follow
a book. I do need the book, but by actually working on the covered topics I 
internalize the knowledge much better.


### Static methods?

If you would make the method `static` (thus being able to allocate it without
declaring a variable), normally you would place the `static` keyword before 
the method. If you try that with a template class you'll get an error:

    error: a storage class can only be specified for objects and functions

For a template the static keyword is not required. The following:

    Ingredient> Beer(Milliliters(30), "Beer");
    Ingredient> Whiskey(Milliliters(15), "Whiskey");

works without issues. With the above code it prints:

    Ingredient name: Beer, amount: 30
    Ingredient name: Whiskey, amount: 15




### The code

This was my example template experiment code, after I fixed the error:

    #include 

    template 
    class Amount {
    public:
        T m_amount;

        Amount(T amount) : m_amount(amount) {}

        friend std::ostream &operator<<(std::ostream &out, const Amount &amount) {
            out << amount.m_amount;
            return out;
        }
    };

    template 
    class Grams : public Amount {
    public:
        Grams(T amount) : Amount(amount) {}
    };

    template 
    class Milliliters : public Amount {
    public:
        Milliliters(T amount) : Amount(amount) {}
    };


    template 
    class Ingredient {
    public:
        Amount m_amount;
        std::string m_name;
        Ingredient(Amount amount, std::string name) : m_amount(amount), 
        m_name(name)
        {
            std::cout << "Ingredient name: " << m_name << ", amount: " << m_amount << "\n";
        }
    };

    template 
    class Oven {
    public:
        Ingredient m_ingredient1;
        Ingredient m_ingredient2;
        Oven(Ingredient ingredient1, Ingredient ingredient2) :
        m_ingredient1(ingredient1),
        m_ingredient2(ingredient2)
        {
            std::cout << "Bowl with ingr1: " << m_ingredient1.m_name << ": " << 
            m_ingredient1.m_amount << "\n";
            std::cout << "          ingr2: " << m_ingredient2.m_name << ": " << 
            m_ingredient2.m_amount << "\n";
        }

        void Grill() {
            std::cout << "Grilling all ingredients in the oven.\n";
        }

    };

    int main() {

        Milliliters amount_water {10};
        Milliliters amount_milk {5.5};
        Grams amount_flour {5.6};
        Grams amount_butter {250};

        std::string name_water { "water" };
        std::string name_milk { "milk" };
        std::string name_flour { "flour" };
        std::string name_butter { "butter" };

        Ingredient> Milk {amount_milk, name_milk};
        Ingredient> Butter {amount_butter, name_butter};

        Oven, Grams> oven1 {Milk, Butter};

        oven1.Grill();

        return 0;
    } 


[1]: https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-interfaces/
[2]: https://www.fluentcpp.com/2018/04/06/strong-types-by-struct/

---

License:
All the text on this website is free as in freedom unless stated otherwise. 
This means you can use it in any way you want, you can copy it, change it 
the way you like and republish it, as long as you release the (modified) 
content under the same license to give others the same freedoms you've got 
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for 
advertisements. You are tracked and Google knows everything about you. 
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license 
unless already licensed under a license which does not allows this form 
of licensing or if another license is stated on that page / in that software:

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .

Just to be clear, the information on this website is for meant for educational 
purposes and you use it at your own risk. I do not take responsibility if you 
screw something up. Use common sense, do not 'rm -rf /' as root for example. 
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.