Jherico
Jherico

Reputation: 29240

Should const functionality be expanded?

EDIT: this question could probably use a more apropos title. Feel free to suggest one in the comments.

In using C++ with a large class set I once came upon a situation where const became a hassle, not because of its functionality, but because it's got a very simplistic definition. Its applicability to an integer or string is obvious, but for more complicated classes there are often multiple properties that could be modified independently of one another. I imagine many people forced to learn what the mutable keyword does might have had similar frustrations.

The most apparent example to me would be a matrix class, representing a 3D transform. A matrix will represent both a translation and a rotation each of which can be changed without modifying the other. Imagine the following class and functions with the hypothetical addition of 'multi-property const'.

class Matrix {
     void translate(const Vector & translation) const("rotation");
     void rotate(const Quaternion & rotation) const("translation");
}

public void spin180(const("translation") & Matrix matrix);
public void moveToOrigin(const("rotation") & Matrix matrix);

Or imagine predefined const keywords like "_comparable" which allow you to define functions that modify the object at will as long as you promise not to change anything that would affect the sort order of the object, easing the use of objects in sorted containers.

What would be some of the pros and cons of this kind of functionality? Can you imagine a practical use for it in your code? Is there a good approach to achieving this kind of functionality with the current const keyword functionality?

Bear in mind

EDIT: In response to SBK's comment about member markup, I would suggest that you don't have any. For classes / members marked const, it works exactly as it always has. For anything marked const("foo") it treats all the members as mutable unless otherwise marked, leaving it up to the class author to ensure that his functions work as advertised. Besides, in a matrix represented as a 2D array internally, you can't mark the individual fields as const or non-const for translation or rotation because all the degrees of freedom are inside a single variable declaration.

Upvotes: 1

Views: 322

Answers (7)

Michael Ekstrand
Michael Ekstrand

Reputation: 29090

It could be interesting, but one of the useful features of const's simple definition is that the compiler can check it. If you start adding arbitrary constraints, such as "cannot change sort order", the compiler as it stands now cannot check it. Further, the problem of compile-time checking of arbitrary constraints is, in the general case, impossible to solve due to the halting problem. I would rather see the feature remain limited to what can actually be checked by a compiler.

There is work on enabling compilers to check more and more things — sophisticated type systems (including dependent type systems), and work such as the that done in SPARKAda, allowing for compiler-aided verification of various constraints — but they all eventually hit the theoretical limits of computer science.

Upvotes: 1

Samuel Danielson
Samuel Danielson

Reputation: 5471

Refinement

When an ADT is indistinguishable from itself after some operation the const property holds for the entire ADT. You wish to define partial constness.

In your sort order example you are asserting that operator< of the ADT is invariant under some other operation on the ADT. Your ad-hoc const names such as "rotation" are defined by the set of operations for which the ADT is invariant. We could leave the invariant unnamed and just list the operations that are invariant inside const(). Due to overloading functions would need to be specified with their full declaration.

void set_color (Color c) const (operator<, std::string get_name());
void set_name  (std::string name) const (Color get_color());

So the const names can be seen as a formalism - their existence or absence doesn't change the power of the system. But 'typedef' could be used to name a list of invariants if that proves useful.

typedef const(operator<, std::string get_name()) DontWorryOnlyNameChanged;

It would be hard to think of good names for many cases.

Usefulness

The value in const is that the compiler can check it. This is a different kind of const.

But I see one big flaw in all of this. From your matrix example I might incorrectly infer that rotation and translation are independent and therefore commutative. But there is an obvious data dependency and matrix multiplication is not commutative. Interestingly, this is an example where partial constness is invariant under repeated application of one or the other but not both. 'translate' would be surprised to find that it's object had been translated due to a rotation after a previous translation. Perhaps I am misunderstanding the meaning of rotate and translate. But that's the problem, that constness now seems open to interpretation. So we need ... drum roll ... Logic.

Logic

It appears your proposal is analogous to dependent typing. With a powerful enough type system almost anything is provable at compile time. Your interest is in theorem provers and type theory, not C++. Look into intuitionistic logic, sequent calculus, Hoare logic, and Coq.

Now I've come full circle. Naming makes sense again,

int times_2(int n) const("divisible_by_3");

since divisible_by_3 is actually a type. Here's a prime number type in Qi. Welcome to the rabbit hole. And I pretended to be getting somewhere. What is this place? Why are there no clocks in here?

Upvotes: 3

B.S.
B.S.

Reputation: 1515

I don't think that you can achieve this as strictly compile-time functionality.

I can't think of a good example so this strictly functional one will have to do:

struct Foo{
    int bar;
};

bool operator <(Foo l, Foo r){
    return (l.bar & 0xFF) < (r.bar & 0xFF);
}

Now I put a some Foos into a sorted set. Obviously the lower 8 bits of bar must remain unchanged so that the order is preserved. The upper bits can however be freely changed. This means the Foos in the set aren't const but aren't mutable either. However I don't see any way you could describe this level of constness in a general useful form without using runtime checking.

If you formalized the requirements I could even imagine, that you could prove that no compiler capable of doing this (at compile time) could even exist.

Upvotes: 1

Loki Astari
Loki Astari

Reputation: 264381

Scott Meyers was working on a system of expanding the language with arbitary constraints (using templates).

So you could say a function/method was Verified,ThreadSafe (etc or any other constraints you liked). Then such constrained functions could only call other functions which had at least (or more) constraints. (eg a method maked ThreadSafe could only call another method marked ThreadSafe (unless the coder explicitly cast away that constraint).

Here is the article:
http://www.artima.com/cppsource/codefeatures.html

The cool concept I liked was that the constraints were enforced at compile time.

Upvotes: 6

Tyler McHenry
Tyler McHenry

Reputation: 76660

I don't think the core language, and especially the const keyword, would be the right place for this. The concept of const in C++ is meant to express the idea that a particular action will not modify a certain area of memory. It is a very low-level idea.

What you are proposing is a logical const-ness that has to do with the high-level semantics of your program. The main problem, as I see it, is that semantics can vary so much between different classes and different programs that there would be no way for there to be a one-size-fits all language construct for this.

What would need to happen is that the programmer would need to be able to write validation code that the compiler would run in order to check that particular operations met his definition of semantic (or "logical") const-ness. When you think about it, though, such code, if it ran at compile-time, would not be very different from a unit test.

Really what you want is for the compiler to test whether functions adhere to a particular semantic contract. That's what unit tests are for. So what you're asking is that there be a language feature that automatically runs unit tests for you during the compilation step. I think that's not terribly useful, given how complicated the system would need to be.

Upvotes: 0

T.E.D.
T.E.D.

Reputation: 44804

In cases where you have groups of members that are either const together or mutable together, wouldn't it make as much sense to formalize that by putting them in their own class together? That can be done today without changing the language.

Upvotes: 5

Jonathan Graehl
Jonathan Graehl

Reputation: 9301

Such high level concepts are useful for a programmer.

If I wanted to make const-ness fine-grained, I'd do it structurally:

struct C { int x; int y; };

C const<x> *c;
C const<x,y> *d;
C const& e;
C &f;

c=&e; // fail, c->y is mutable via c
d=&e;
c=&f;
d=c;

If you allow me to express a preference for a scope that maximally const methods are preferred (the normal overloading would prefer the non-const method if my ref/pointer is non-const), then the compiler or a standalone static analysis could deduce the sets of must-be-const members for me.

Of course, this is all moot unless you plan on implementing a preprocessor that takes the nice high-level finely grained const C++ and translates it into casting-away-const C++. We don't even have C++0x yet.

Upvotes: 1

Related Questions