Read-only data members

  • Thread starter David Gausebeck
  • Start date
D

David Gausebeck

The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now. You can always get
around it with accessor methods that return const references, but
classes with huge lists of accessors often seem awkward/ugly.

So I finally sat down and thought of a syntax I'd like to see as a
fix. I certainly don't expect to ever see it introduced, but it's
interesting to think about.

The syntax is to add a new keyword, "expose", which can only be used
in class declarations. After expose, you redeclare an existing class
member with a greater degree of const-ness, at a different protection
level. It might be necessary to only expose a member after that
member's declaration, but for this example I'll assume you can use it
before:

class Foo {
public:
// public can read the string contents
expose const char* const str;
protected:
// derived classes can modify the string contents
expose char* const str;
private:
// only Foo can change str itself
char* str;
};

My questions for the group are:
1) Would compilers have any problem handling this syntax and its
effects? It seems like it would be relatively easy to handle, but
I don't know all that much about compilers.
2) Are there any flaws with the syntax or its effects? i.e. would it
lead to any ambiguous situations or have undesirable side effects?
3) Do you think that, if this syntax were magically added to C++, it
would be a good/useful thing or a bad/useless thing?

-Dave
 
J

jeffc

David Gausebeck said:
The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now.

I couldn't understand from your post why "const" doesn't provide read-only
access.
 
A

Agent Mulder

Nice point. I hacked out the code

<David Gausebeck>
class Foo {
public:
// public can read the string contents
expose const char* const str;
protected:
// derived classes can modify the string contents
expose char* const str;
private:
// only Foo can change str itself
char* str;
};
</David Gausebeck>

Expose only works for protected, doesn't it?
In the public section, data is already 'exposed'.
In the private section, there is nothing to expose.
So it dresses up protected. More logical to introduce
a 4th access specifier to handle 'protected' data members.
It must be something starting with a p (I guess). Perhaps
priviliged?

-X
 
D

David Gausebeck

The inability to denote data members as read-only in C++ is something
I couldn't understand from your post why "const" doesn't provide read-only
access.

It does, but not in the way I'm talking about. I realized that line
was unclear, but I couldn't think of a better, concise way to write
it. So I just went on and explained in more detail below. Having
thought about it a little more now, it would be better as:

The inability to denote data members as read-only for public or
protected access is something which has annoyed me (slightly) for a
while now.

-Dave
 
G

Gianni Mariani

David said:
The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now. You can always get
around it with accessor methods that return const references, but
classes with huge lists of accessors often seem awkward/ugly.
....
-Dave

I think the accessor method technique is exactly the right solution.

The alternative is to make the members themselves "smart members".

This is an example, I'm sure I missed a few important things, I'll let
you fill in the blanks.


class Foo;

template <typename T> class ReadOnlyMember
{
friend class Foo;

public:

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

protected:

template <typename T1> const T & operator = ( T1 & vin )
{
value = vin;
return value;
}

ReadOnlyMember( const T & vin )
: value( vin )
{
}

T value;
};


class Foo {
public:
// public can read the string contents
ReadOnlyMember<char*> str1;

// derived classes can modify the string contents
ReadOnlyMember<const char*> str2;

private:
// only Foo can change str itself
char* str3;

public:

Foo()
: str1( "str1" ), str2( "str2" )
{
}

void tester()
{
char xxx[2];
str1 = xxx;
str2 = "BBB";
}

};


int main()
{

Foo f;

const char * xx = f.str1;

f.str2 = "can't do this";
}
 
D

David Gausebeck

Nice point. I hacked out the code
<David Gausebeck>
class Foo {
public:
// public can read the string contents
expose const char* const str;
protected:
// derived classes can modify the string contents
expose char* const str;
private:
// only Foo can change str itself
char* str;
};
</David Gausebeck>

Expose only works for protected, doesn't it?

It could be used for either protected or private members.
In the public section, data is already 'exposed'.

Yes, anything declared as public couldn't be exposed further.
In the private section, there is nothing to expose.

What do you mean? In the example above, it's a private member that's
being exposed.
So it dresses up protected. More logical to introduce
a 4th access specifier to handle 'protected' data members.
It must be something starting with a p (I guess). Perhaps
priviliged?

Even if it did only apply to protected, a different specifier
(meaning, presumably, protected but readable by public) wouldn't be
sufficient, because it couldn't differentiate degrees of const-ness.
Per the example above, would a 'privileged' char* be accessible to
public as const char* const, or just as char* const?

-Dave
 
J

jeffc

David Gausebeck said:
It does, but not in the way I'm talking about. I realized that line
was unclear, but I couldn't think of a better, concise way to write
it. So I just went on and explained in more detail below. Having
thought about it a little more now, it would be better as:

The inability to denote data members as read-only for public or
protected access is something which has annoyed me (slightly) for a
while now.

Yes, I see your point.
 
J

jeffc

Ioannis Vranos said:
I do not think such a feature is badly needed and i also believe that we
must not hurry add non-badly needed features into the language.

Well don't worry - "they" won't be hurrying to add any features "we" ask for
:)
 
P

Peter Ammon

David said:
The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now.

[...]

How about (my C++ is a little rusty so bear with me)

class Foo {
int somePrivateValue;
public:
const int & readOnlyWay;
Foo();
};

Foo::Foo() : readOnlyWay(somePrivateValue) {
somePrivateValue = 10;
}

This provides a public read only accessor to somePrivateValue via the
instance variable readOnlyWay, but the class itself is free to modify
somePrivateValue.

-Peter
 
I

Ioannis Vranos

Peter Ammon said:
David said:
The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now.

[...]

How about (my C++ is a little rusty so bear with me)

class Foo {
int somePrivateValue;
public:
const int & readOnlyWay;
Foo();
};

Foo::Foo() : readOnlyWay(somePrivateValue) {
somePrivateValue = 10;
}

This provides a public read only accessor to somePrivateValue via the
instance variable readOnlyWay, but the class itself is free to modify
somePrivateValue.


Yeap, that's it. You did it. :) One could use a const * too. Personally i
prefer data hiding for inside class non-constants.







--
Ioannis

* Programming pages: http://www.noicys.freeurl.com
* Alternative URL 1: http://run.to/noicys
* Alternative URL 2: http://www.noicys.cjb.net
 
D

David Gausebeck

David said:
The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now.

[...]

How about (my C++ is a little rusty so bear with me)

class Foo {
int somePrivateValue;
public:
const int & readOnlyWay;
Foo();
};

Foo::Foo() : readOnlyWay(somePrivateValue) {
somePrivateValue = 10;
}

This provides a public read only accessor to somePrivateValue via the
instance variable readOnlyWay, but the class itself is free to modify
somePrivateValue.

That's pretty good; I hadn't thought of that. It's still awkward
enough that I'd prefer to use accessor methods, though.

The disadvantages I see are:
1) the accessor reference must have a different name from the variable
to which it provides access.
2) the references will make the object larger (typically 4 bytes each)
3) the reference initializer list must be updated separately, so you
have to edit both a .h and a .cpp to grant access. Further, the
reference initializer list would have to be repeated for every
constructor.

I consider 1) to be the most significant problem... it's the same
reason I'm not entirely happy with accessor methods: they
unnecessarily obfuscate what's really going on. Maybe only a little
bit, but every little bit hurts.

-Dave
 
R

Risto Lankinen

Hi!

David Gausebeck said:
The inability to denote data members as read-only for public or
protected access is something which has annoyed me (slightly) for a
while now.

Put the variables in private section, and add const references in the
public section for all variables that you want to expose:

class C
{
private:
int i;
char *p;

public:
int const &expose_i;
char * const &expose_p;

// etc...
};

BTW, this is actually an argument for making the declaration
level initializer legal for [at least] reference members, like it is
already for static members. If the initializer was allowed to be
present in the class declaration, as shown below...

public:
int const &expose_i = i;
char * const &expose_p = p;

.... then the compiler could optimize the references away from
the run-time manifestation of the object altogether, since all users
of the class would have to include the class declaration anyway,
and all translation units would therefore know at all times what
actual data item the reference will alias. Since this could not
vary, it would not need to be stored in the object itself.

Cheers!

- Risto -
 
A

Arbesto Pelta

The inability to denote data members as read-only in C++ is something
which has annoyed me (slightly) for a while now. You can always get
around it with accessor methods that return const references, but
classes with huge lists of accessors often seem awkward/ugly.

So I finally sat down and thought of a syntax I'd like to see as a
fix. I certainly don't expect to ever see it introduced, but it's
interesting to think about.

The syntax is to add a new keyword, "expose", which can only be used
in class declarations. After expose, you redeclare an existing class
member with a greater degree of const-ness, at a different protection
level. It might be necessary to only expose a member after that
member's declaration, but for this example I'll assume you can use it
before:

class Foo {
public:
// public can read the string contents
expose const char* const str;
protected:
// derived classes can modify the string contents
expose char* const str;
private:
// only Foo can change str itself
char* str;
};

My questions for the group are:
1) Would compilers have any problem handling this syntax and its
effects? It seems like it would be relatively easy to handle, but
I don't know all that much about compilers.
2) Are there any flaws with the syntax or its effects? i.e. would it
lead to any ambiguous situations or have undesirable side effects?
3) Do you think that, if this syntax were magically added to C++, it
would be a good/useful thing or a bad/useless thing?

-Dave

I think that the an accessor method offer an advantage: it isolate class
interface from attributes implementation.

You now use a char* to the str attribute but tomorrow maybe you want to use
a stl string and then you will have a problem with your str 'direct
exposition'.

Now : const char* GetStr() const { return str; }
Tomorrow : const char* GetStr() const { return str.c_str(); }

-Arbesto
 
I

Ioannis Vranos

Risto Lankinen said:
Put the variables in private section, and add const references in the
public section for all variables that you want to expose:

class C
{
private:
int i;
char *p;

public:
int const &expose_i;
char * const &expose_p;

// etc...
};

BTW, this is actually an argument for making the declaration
level initializer legal for [at least] reference members, like it is
already for static members. If the initializer was allowed to be
present in the class declaration, as shown below...

public:
int const &expose_i = i;
char * const &expose_p = p;

... then the compiler could optimize the references away from
the run-time manifestation of the object altogether, since all users
of the class would have to include the class declaration anyway,
and all translation units would therefore know at all times what
actual data item the reference will alias. Since this could not
vary, it would not need to be stored in the object itself.


Since there is a reference for every object you create, why it would be able
to optimise the references away and why it cannot do it while using a
constructor? Providing the above ability seems to me that two ways of object
construction would be provided complicating the construction/destruction
rules.

Using constructors we have simple rules and much control on the construction
of an object.







--
Ioannis

* Programming pages: http://www.noicys.freeurl.com
* Alternative URL 1: http://run.to/noicys
* Alternative URL 2: http://www.noicys.cjb.net
 
R

Risto Lankinen

Hi!

Ioannis Vranos said:
Since there is a reference for every object you create, why it would be able
to optimise the references away and why it cannot do it while using a
constructor?

The compiler could, for every dereference of 'expose_i'
simply compile a dereference of 'i' without having to store
the actual reference into the object itself. This can take
place in any compilation unit that includes the class header
containing the hypothetical initializer. This optimization
will be robust only if all compilation units implement it
identically (e.g. no other compilation parameter, such as
an optimization-level switch, is a factor of whether this
optimization were applied in a particular instance, or not).

The constructor-based member initialization will bury the
initializations to wherever the constructor is implemented,
which [typically] will not be visible to the compilation unit
that uses the class, hence the only way to communicate the
value of the initializer to the users of the class is to expend
a data item for it. Furthermore, the compiler would have
to scan multiple constructors to find out if the initialization
is indeed identical in all of them (a requirement for this
optimization to work).

In summary, allowing class-based reference initialization (as
opposed to the existing instance-based) would provide the
compiler with an opportunity to optimize away the storage
required by the initializer value, whenever its value can be
deterministically [re]calculated in compile-time without side
effects.

Cheers!

- Risto -
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,276
Latest member
Sawatmakal

Latest Threads

Top