Boost logo

Boost :

Subject: Re: [boost] Read only attributes in C++ (class proposal)
From: Francois Duranleau (xiao.bai.xiong_at_[hidden])
Date: 2016-10-17 08:37:58


On Mon, Oct 17, 2016 at 5:36 AM, Daniel Illescas Romero
<elamericano_at_[hidden]> wrote:
> Hello, I wanted to make some read only attributes on C++ so I made a class, named ReadOnly. This class will contain an internal private value, only “friend” classes could change this internally.
>
> Description:
>
> · A read only object will contains an internal value that can’t be changed outside the class. For example: ReadOnly<string> name = “Daniel”;
> To access its value: object.name, if you try: object.name = “other”, it won’t change it’s value because it’s a read only variable; you can change it’s value only inside the class the object is declared, you could change it like: name.value = “whatever”;
>
> · To set a value (outside the class) to a read only attribute you MUST specify a setter function (which can be static and contained inside the class), like:
> ReadOnly<int> age {ReadOnly::setAge, 1}; (optionally you can also set a default value to the attribute).
>
> Example of setter function:
>
> static bool setAge(const int& value) {
> return (value > 0 && value <= 150);
> }
>
> So if you you have an object and you want to change its value like this: object.age = 100; it will change its internal value only if the setter function (which is called internally) returns true.
>
> · The idea of doing all of this instead private variables and getters & setters is because this approach is closer to OOP than what is normally used; I mean if you have an object and you want to get an attribute value, what you need to do in most cases is something like: object.getAttribute(), you are calling a function to just take a look at an attribute, that shouldn’t make sense in OOP, that’s why I made all of this.
> It’s also similar to how you manage getters and setters in Swift, because they are embedded in the attribute and you don’t need to make other functions. :)
>
> I uploaded the project to github so everyone can take a look: https://github.com/illescasDaniel/ReadOnly <https://github.com/illescasDaniel/ReadOnly>
> —
>
> I wanted to know your opinion about the idea of the class and also about the implementation, I’d love to upload it to boost libraries but I want to know people opinion and make the class better.

With your ReadOnly class, what you gain in syntax sugar, you loose in
performance and memory. I am not really worried about the performance
of setting the value, because the number of gets is usually much
greater than the number of sets, but you inccur an overhead of one
function pointer (and a bool, may or may not matter depending on
alignment of the wrapped type) per member, and you penalize data
locality. For the occasional object here and there, no problem, but
one should not abuse this. Say I have a class Color that has red,
green, and blue properties, as floats, and I want to ensure all values
lie in the [0.0, 1.0] interval. If I use ReadOnly on a 64-bit
architecture, I end up using 48 bytes for the class (extra setters
member + alignment) instead of 12 bytes! Now, if I create a, say,
vector<Color> of several thousands of elements for processing, I not
only eat up more memory, I get four times more cache misses! So one
must be aware of the cost of using ReadOnly and evaluate if it is
acceptable for their use case.

A note aside, instead of having the setter only by a predicate, it
could do the whole assignment, e.g.

static void setColorComponent(float& c, float value)
{
    c = std::min(1.0f, std::max(0.0f, value));
}

static void setAge(int& a, int value)
{
    if(value <=0 || value > 150)
    {
        throw invalid_age(value); // or whatever error reporting you want
    }
    else
    {
        a = value;
    }
}

This way, you can modify the input value instead of just refusing
them. Plus, the user of ReadOnly has the full power to report errors
as he/she wishes. And the default setter could become

[](Type&, const Type&)
{
    // report an error: exception, logging, whatever
}

With that you don't even need the boolean hasSetter member!

-- 
François

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk