C# properties in C++ (sort of)

Today I decided I was tired of writing tedious accessor/mutator boilerplate, so I wrote a couple of classes to emulate C# properties. You can do something similar with:
1
2
3
4
5
6
7
8
class foo {
public:
    const int& readonly;

    foo() : readonly(m_readonly) {}
private:
    int m_readonly;
};

But the advantage of C#'s properties is that you can, if you want, handle access and mutation however you want - you can write custom get and set methods for the property:
1
2
3
4
5
6
7
8
9
10
11
12
class Foo {
    public int TimesAccessed {
        get { return mTimesAccessed; }
    }

    public int ReadOnly {
        get { ++mTimesAccessed; return mReadOnly; }
    }

    private int mTimesAccessed;
    private int mReadOnly;
}

That is what this code allows. It also has default accessor and mutator functors if you want simple properties so that you can switch to using your own accessors and mutators later on without affecting client code. There are also operator= overloads for properties as l- and r-values so that you can use them as though they were ordinary values. It's also header-only.

There is some overhead, though - currently the accessor and mutators store a reference each to your data, and it also contains its own object of type T which is used if you don't pass a reference to one. There's also overhead in translating the property::get/set methods into the accessor and mutator functor methods. Still, though, I'd say that, in general, flexibility and clarity are more important than performance, programmer time is more expensive than CPU time, and faster algorithms are superior to more optimised code.

Here is the code and an example: http://ideone.com/cRYmS0

Thoughts? Suggestions?

Ultimately, I don't think I've reduced boilerplate at all (I may even have done the opposite), but whatever.

Here's my to-do list, ordered by priority. Maybe someone can help me with them.
1. Allow assignment from a property to any object of its contained type (the reverse is easy enough and is done).
2. Allow assignment from any readonly property to any writeable property with the same contained type
3. Make readonly/readwrite a template parameter instead of having separate classes, making the syntax prettier (would also solve #2).

4. Reduce memory overhead somehow.
5. Reduce need for boilerplate code (that was, after all, the point of the class...).
Last edited on
New version which solves #2 and #3 on the to-do list: http://ideone.com/hxtHnt
closed account (o1vk4iN6)
But the advantage of C#'s properties is that you can, if you want, handle access and mutation however you want - you can write custom get and set methods for the property:

1
2
3
public int ReadOnly {
        get { ++mTimesAccessed; return mReadOnly; }
    }



I actually don't really like the way C# handles get and setters like that. This is actually a good example of how it can be abused.

I would never expect this to modify anything in the class.

 
int ro = foo.ReadOnly;


It has the same syntax as simply accessing a variable so you can easily misinterpret what this is actually doing. Not familiar with C# naming convention for this so maybe it isn't that big of an issue.

As for the C++ code, you need to handle ~all~ cases, as an example if you copy your class right now the reference will point to a wrong address. I think LB does something similar and fixes this simply by using a C++11 feature:

1
2
3
4
5
6
7
8
9
class Foo
{
public:
    const int& readonly = m_readonly; // will now be correct for any constructor/copying

private:
    int m_readonly;

};



As for the example you posted, the intention was to make accessors and such easier to handle but do you really think that your my_accessor class is easier than just:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

class Foo
{
public:
    const int& GetAndCountReadOnly() /*const*/ { ++m_counter; return m_readonly; }

};

// Versus //

class my_accessor {
public:
    my_accessor(const int& value_ref, int& counter_ref) : m_value_ref(value_ref), m_counter_ref(counter_ref)
    {
        // Do nothing.
    }

    const int& operator()() noexcept
    {
        ++m_counter_ref;
        return m_value_ref;
    }
private:
    const int& m_value_ref;
    int& m_counter_ref;
};


I think you are over complicating something simply because it was implemented differently in another language.

Last edited on
I like properties -- but they really have to be a language feature to work seamlessly. I've written half a gazillion property classes for C++ -- some of them really comprehensive -- but in the end they really aren't worth the trouble over the usual way of doing things. And they always require a proxy object.

These days, when I want something of a property, I'll just provide overloaded methods as getter/setter. The advantage is also in named argumentation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct foo
{
private:
  int m_quux;

public:
  foo& quux( int x )  // setter -- force x in [-7,7]
  {
    m_quux = std::max( -7, std::min( x, 7 ) );
    return *this;
  }

  int quux() const  // getter
  {
    return m_quux;
  }
};
1
2
foo z = foo().quux( 5 );
cout << z.quux() << endl;

There is no way, of course, to pass a reference to some modifier function, but you can't do that with properties anyway -- at least not without yet another temporary object, which you can create as a small templated class.

The reason that is a problem is because you don't directly control the lifetime of temporary objects, and so it leaves the door open for race conditions when accessing a value. (For most accesses that is not an issue, of course, but it'll bite you when you try to do anything comprehensive.)

That's my $0.02.
@Duoas
I do the same thing, I was just experimenting with something like C#'s properties because I don't like the boilerplate you have to write with getters and setters. Ultimately, though, my class ended up requiring much more boilerplate, so it was a total failure, really :P

@xerzi
You're right, it is overcomplicating it and it is kind of pointless. This is just what I wasted my time doing today. Incidentally I didn't know C++11 allowed you to assign references outside of constructors.

This is actually a good example of how it can be abused.

Here's a more practical example of when you might need something more complex in your getter or setter:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct viewport {
    vec2i offset;
    vec2u size;
};

class window {
public:
    void viewport(const viewport& value) noexcept
    {
        glViewport(value.offset.x(), value.offset.y(), value.size.x(), value.size.y());
        m_viewport = value;
    }

    const viewport& window::viewport() const noexcept
    {
        return m_viewport;
    }
private:
    viewport m_viewport;
};

As for read-access modifying something in the class, you're right that it would be unexpected and probably not a good idea in some - maybe even most - cases, but it can be useful for transparently implementing reference counting, for example. I think it's a matter of judgement. It certainly shouldn't be able to cause any "gotcha" situations unless it's totally unavoidable - but the same problem can occur with C++ getters and setters, anyway. As far as I know there aren't any naming conventions in C# that differentiate properties from regular variables -- they're syntactically identical by design.
Last edited on
Seems to me like the best way to mimic C# style properties (which I was never really crazy about, but whatever) would be to do tricks with operator and cast overloading.

Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
template typename<T>
class Property
{
public:
    inline operator T () const { /* do a read */ }
    inline Property<T>& operator = (const T& v) { /* do a write */ }

    // possibly +=, |=, >>= etc etc operators if you want to get snazzy.
};

//...

class MyClassWithProperties
{
public:
    Property<int>   foo;
    Property<std::string>  bar;
};



The tricky part then becomes doing read/write callbacks.

You might be able to easily do this with C++11 style in-class construction of objects. I'm fuzzy on what exactly is legal here...

1
2
3
4
5
6
7
8
9
class MyClass
{
public:
    Property<int>  foo(this, &MyClass::get_foo, &MyClass::set_foo);

private:
    void MyClass::get_foo(int& v) const { /* read */ }
    void MyClass::set_foo(int& v, const int& new_v) { /* write */ }
};


If you can get that working... the boiler plate can be FURTHER reduced with macros:

1
2
3
4
5
6
7
8
class MyClass
{
public:
    PROPERTY(int, foo, MyClass);

private:
    // add getters and setters
};




But the real question here is.... "Is this worth it?"

It's an interesting novelty, but ultimately you're just trying to morph one language into another which is usually a bad idea.

Personally I find the need for direct getters/setters to be so rare that I don't mind doing the overhead for the few times I need to write one.

EDIT: bah... ninja'd by like 3 people.
Last edited on
@Disch
I looked into something like this after finishing up the second version:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
template <typename T>
class property {
public:
    property& get(std::function<T()> function)
    {
        getter = function;
        return *this;
    }

    property& set(std::function<void(const T&)> function)
    {
        setter = function;
        return *this;
    }

    const T& operator T() const
    {
        return getter();
    }

    void operator=(const T& value)
    {
        setter(value);
    }
private:
    std::function<const T&(void*)> getter;
    std::function<void(void*, const T&)> setter;
};

So I could do something like
1
2
3
4
5
6
7
8
9
10
property<int> x(*this)
    .get([](void* pthis) {
        foo* this = static_cast<foo*>(pthis);
        return this->m_x;
    })
    .set([](void* pthis, int value) {
        foo* this = static_cast<foo*>(pthis);
        this->m_x = value;
    })
;

which was much more successful in terms of extending C++'s syntax and reducing boilerplate, but ultimately I couldn't get it working. I had wanted something like that from the get-go (I got the idea when I was using Boost.Python and Boost.Program_options and I wanted to do something similar, I really like the way Boost libraries sometimes use strange [but still intuitive] syntax like that) but somehow I got lost on the way.

Incidentally, I didn't know about operator T() before, so thanks for that.
Topic archived. No new replies allowed.