Const_cast as undefined behavior?

N

Nephi Immortal

I thought I want to add. Let’s look at my code below.

int main()
{
const char* data = "0123456789";

const_cast< char* >( data )[ 2 ] = 'A';

return 0;
}

C++ Compiler will compile without any problems, but it is unaware of the 4Kmemory page if it is marked as read only, but not read-write. The undefined behavior is possible. If you use Microsoft’s Virtual Memory functionsor Heap Memory functions, you have to be careful to mark either read only or read-write.

The Visual C++ will automatically compiles all constant strings into 4K memory page and it marks them as read only. What happen if you take away constant string by using const_cast? The C++ Compiler will compile fine, but the program will crash during the execution. The error dialog box will appear to the screen as saying violation memory access. You are not permitted to modify constant strings in read only 4K memory page.

I am supposed to take your advice. I should use two classes as const classand non-const class instead of one const / non-const class.
 
B

Bart van Ingen Schenau

I am supposed to take your advice. I should use two classes as const
class and non-const class instead of one const / non-const class.
You can still use only one class, but you have to ensure it can not be
mis-used to modify a const object.

You can do this simply by changing the return value of X::Get to be 'const
Y', like this:

class Y;

class X
{
friend class Y;

public:
X( int data1, int data2 );
const Y Get() const;
Y Set();

private:
int data1;
int data2;
};

class Y
{
public:
Y( X& p );
Y( const Y& r );

int Value1() const;
void Value1( int data );

int Value2() const;
void Value2( int data );

private:
X& pX;
};

X::X( int data1, int data2 ) : data1( data1), data2( data2 ) {
}

const Y X::Get() const
{
return Y( const_cast< X& >( *this ));
}

Y X::Set()
{
return Y( *this );
}

Y::Y( X& p ) : pX( p )
{
}

Y::Y( const Y& r ) : pX( r.pX )
{
}

int Y::Value1() const
{
return pX.data1;
}

void Y::Value1( int data )
{
pX.data1 = data;
}

int Y::Value2() const
{
return pX.data2;
}

void Y::Value2( int data )
{
pX.data2 = data;
}

int main()
{
int number1 = 0, number2 = 0;

const X x1( 5, 10 );
X x( 5, 10 );

number1 = x.Get().Value1();
number2 = x.Get().Value2();

x.Set().Value1( 50 );
//x.Get().Value2( 100 ); /* error: You can only use const members of
Y. That excludes Value1(number) and Value2(number) */

number1 = x1.Get().Value1();
number2 = x1.Get().Value2();

number1 = x1.Get().Value1();
number2 = x1.Get().Value2();

//x1.Set().Value1( 50 ); /* error: You can only use const members of
X. That excludes Set() */
//x1.Get().Value2( 100 ); /* error: You can only use const members of
Y. That excludes Value1(number) and Value2(number) */

number1 = x1.Get().Value1();
number2 = x1.Get().Value2();

return 0;
}
 
S

Stuart

Andrey said:
That's not right. What may lead to undefined behavior is having used
const_cast to remove access restrictions which should always be there and
should never be taken away. If the const qualifier isn't used irresponsibly
then the compiler naturally catches the attempt to call non-const member
functions on a const object, as is expected from him.

Let's be honest here: if you one day stumbled on a bug caused by this sort
of problem, you would never claim that the const_cast was perfectly fine,
and that the entire problem was caused by how the "write operation" has been
poorly designed so that it fails to work when its access restrictions were
removed.

From this perspective you are right, the source of the evil is
definitely a mis-used const_cast.

However, the C++ Standard chooses to define it a bit differently so that
programmers which have to deal with C APIs can pass const_cast'ed
pointers to the C API without leaving safe ground.

If, however, an implementor of the strlen function decides to
write-access the char* pointer, it would be definitely _his_ fault, not
yours.

This is due to the fact that there is an implied contract between you
and any implementor of strlen: The passed array may only be accessed
through read operations. It's a shortcoming of the C language at the
time that one could not codify this implied contract in the signature of
strlen.

To come to an conclusion: const_casts should be avoided at all costs
unless you are dealing with C APIs. To allow C++ programmers to use C
APIs, not the use of the const_cast should be banned but the use in the
wrong way.

Regards,
Stuart
 
T

Tobias Müller

Nephi Immortal said:
I am supposed to take your advice. I should use two classes as const
class and non-const class instead of one const / non-const class.

Actually, you gain nothing by combining the two proxy classes into one.
You don't need the setter methods on the getter proxy and vice versa:

class Y_Get
{
public:
Y_Get(const X&);
int value1() const;
int value2() const;
private:
const X& x;
};

class Y_Set
{
public:
Y_Set(X&);
void value1(int);
void value2(int);
private:
X& x;
};

It's not really more code to write.

Tobi
 
T

Tobias Müller

Bart van Ingen Schenau said:
You can still use only one class, but you have to ensure it can not be
mis-used to modify a const object.

You can do this simply by changing the return value of X::Get to be 'const Y'

const X x(5,10);
Y y(x.Get());
y.value1(7);

Since Y has a copy constructor it's still not really safe.

Tobi
 
N

Nephi Immortal

const X x(5,10);

Y y(x.Get());

y.value1(7);



Since Y has a copy constructor it's still not really safe.
Why isn't it safe? Is it because of reference instead of value?


I made some changes in my code since you said it is unsafe. I have alreadywrapped Y class into X class. Y class is inaccessible. Also, I have added const in Get() so that it can have two consts and do not need const_cast.

Please review my code below. Let me know if my code is safe.

You may notice that I added explicit copy constructor function and destructor function because you can be able to trace step by step in the debugger.

class X
{
private:
struct Y
{
Y( int data1, int data2 );
Y( const Y& right );
~Y();

int Value1() const;
void Value1( int data );

int Value2() const;
void Value2( int data );

int data1;
int data2;
} data;

public:
X( int data1, int data2 );
~X();

const Y Get() const;
const Y& Get();
Y& Set();
};

X::Y::Y( int data1, int data2 ) : data1( data1 ), data2( data2 )
{
}

X::Y::Y( const Y& right ) : data1( right.data1 ), data2( right.data2 )
{
}

X::Y::~Y()
{
}

int X::Y::Value1() const
{
return data1;
}

void X::Y::Value1( int data )
{
data1 = data;
}

int X::Y::Value2() const
{
return data2;
}

void X::Y::Value2( int data )
{
data2 = data;
}

X::X( int data1, int data2 ) : data( Y( data1, data2 ) )
{
}

X::~X()
{
}

const X::Y X::Get() const
{
return data;
}

const X::Y& X::Get()
{
return data;
}

X::Y& X::Set()
{
return data;
}

int main()
{
int number1 = 0, number2 = 0;

// const X x( 5, 10 );
X x( 5, 10 );

number1 = x.Get().Value1();
number2 = x.Get().Value2();

x.Set().Value1( 50 ); // error if you declare const X x( 5, 10 )
x.Set().Value2( 100 ); // error if you declare const X x( 5, 10 )

number1 = x.Get().Value1();
number2 = x.Get().Value2();

x.Get().Value1( 500 ); // error if you declare const X x( 5, 10 )
x.Get().Value2( 1000 ); // error if you declare const X x( 5, 10 )

return 0;
}
 
T

Tobias Müller

Nephi Immortal said:
Why isn't it safe? Is it because of reference instead of value?

The copy constructor is fine, that's how copy constructors usually are
defined.
But the fact that it exists enables other people to use it.
However you also cannot delete it, because it is needed to return Y by
value in X::Get() and X::Set().
I made some changes in my code since you said it is unsafe. I have
already wrapped Y class into X class.

I think that's a good thing for clarity, but does not fix the problem.
Y class is inaccessible.

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

The only solution I can think of is, make the _constructor_ of Y private
and write "friend class X;" in Y.
Also, I have added const in Get() so that it can have two consts and do
not need const_cast.

Now you have two Get() methods, why? I think you should decide whether to
return by reference or value and then be consequent.

const Y Get() const;
Y Set();

or

const Y& Get() const;
Y& Set();

Tobi
 
B

Bart van Ingen Schenau

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

There is no problem with returning a private type from a public (member)
function.
The only thing that the caller can't do is name the return type.

X::Y y = x.Get(); // Error. X::Y is private withing this context
x.Get().Value1(); // OK. No private names in sight
auto y = x.Get(); // OK. No private names in sight

Bart v Ingen Schenau
 
B

Bart van Ingen Schenau

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

There is no problem with returning a private type from a public (member)
function.
The only thing that the caller can't do is name the return type.

X::Y y = x.Get(); // Error. X::Y is private withing this context
x.Get().Value1(); // OK. No private names in sight
auto y = x.Get(); // OK. No private names in sight

Bart v Ingen Schenau
 
B

Bart van Ingen Schenau

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

There is no problem with returning a private type from a public (member)
function.
The only thing that the caller can't do is name the return type.

X::Y y = x.Get(); // Error. X::Y is private withing this context
x.Get().Value1(); // OK. No private names in sight
auto y = x.Get(); // OK. No private names in sight

Bart v Ingen Schenau
 
B

Bart van Ingen Schenau

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

There is no problem with returning a private type from a public (member)
function.
The only thing that the caller can't do is name the return type.

X::Y y = x.Get(); // Error. X::Y is private withing this context
x.Get().Value1(); // OK. No private names in sight
auto y = x.Get(); // OK. No private names in sight

Bart v Ingen Schenau
 
B

Bart van Ingen Schenau

AFAIK that's not possible. To return an object of type Y in a public
method. Y has to be public too.

There is no problem with returning a private type from a public (member)
function.
The only thing that the caller can't do is name the return type.

X::Y y = x.Get(); // Error. X::Y is private withing this context
x.Get().Value1(); // OK. No private names in sight
auto y = x.Get(); // OK. No private names in sight

Bart v Ingen Schenau
 
N

Nephi Immortal

There is no problem with returning a private type from a public (member)

function.

The only thing that the caller can't do is name the return type.



X::Y y = x.Get(); // Error. X::Y is private withing this context

x.Get().Value1(); // OK. No private names in sight

auto y = x.Get(); // OK. No private names in sight

My code has nothing wrong, but it does fix the solution. Y class is private.
It prevents the client from attempting to create Y variable and invoke its
methods and/or properties.

I wonder if x.Get() is supposed to be void return type instead of Y class
return type or the return type is optional? How do you know C++ Compiler
will compile "auto y = x.Get()" without any problem since Y class is private?
It won't compile!

Please clarify why you think my code does not fix the solution? If return type
is optional and private, then clients are forbidden to use it.
 
B

Bart van Ingen Schenau

My code has nothing wrong, but it does fix the solution. Y class is
private. It prevents the client from attempting to create Y variable and
invoke its methods and/or properties.

It did so in C++03, but no longer in C++11 due to the new use of 'auto'
to create a variable whose type is deduced from its initialiser.
I wonder if x.Get() is supposed to be void return type instead of Y
class return type or the return type is optional? How do you know C++
Compiler will compile "auto y = x.Get()" without any problem since Y
class is private? It won't compile!

I know because I tested it (with GCC 4.7.2), but it uses a feature that
was introduced in the new C++ standard, so it might not be supported by
your compiler if it is an older one.
The fact that class Y is private does not mean that it doesn't exist
outside class X, it only means that you can't say its name outside class
X. So, if you have a way to create a variable without saying its name
(such as the new auto feature), making a type private will not stop you
from creating variables of that type.

Bart v Ingen Schenau
 
N

Nephi Immortal

It did so in C++03, but no longer in C++11 due to the new use of 'auto'

to create a variable whose type is deduced from its initialiser.










I know because I tested it (with GCC 4.7.2), but it uses a feature that

was introduced in the new C++ standard, so it might not be supported by

your compiler if it is an older one.

The fact that class Y is private does not mean that it doesn't exist

outside class X, it only means that you can't say its name outside class

X. So, if you have a way to create a variable without saying its name

(such as the new auto feature), making a type private will not stop you

from creating variables of that type.

Now, I begin to understand the auto feature. Visual C++ 2012 has the auto
feature, but it has the switch to turn it off in order to use traditional
auto.

Possibly, later version as C++14 over C++11 should fix the problem. The C++
Compiler should fail to compile if it detects auto's variable definition
attempts to access private class with the return type.

Take a look at my revised code. It is the last one I have made. The solution
is almost perfect until C++14 becomes available.

Comment your opinion.

class X
{
private:
class Y
{
public:
Y( int data1, int data2 );
Y( const Y& right );
~Y();

int Value1() const;
void Value1( int data );

int Value2() const;
void Value2( int data );

private:
int data1;
int data2;
} data;

public:
X( int data1, int data2 );
~X();

const Y& Get() const;
Y& Set();
};

X::Y::Y( int data1, int data2 ) : data1( data1 ), data2( data2 )
{
}

X::Y::Y( const Y& right ) : data1( right.data1 ), data2( right.data2 )
{
}

X::Y::~Y()
{
}

int X::Y::Value1() const
{
return data1;
}

void X::Y::Value1( int data )
{
data1 = data;
}

int X::Y::Value2() const
{
return data2;
}

void X::Y::Value2( int data )
{
data2 = data;
}

X::X( int data1, int data2 ) : data( Y( data1, data2 ) )
{
}

X::~X()
{
}

const X::Y& X::Get() const
{
return data;
}

X::Y& X::Set()
{
return data;
}

int main()
{
int number1 = 0, number2 = 0;

// const X x( 5, 10 );
X x( 5, 10 );

number1 = x.Get().Value1();
number2 = x.Get().Value2();

auto y = x.Get();

number1 = y.Value1(); // OK -- should fail to compile because of private
number2 = y.Value2(); // OK -- should fail to compile because of private

// number1 = y.data1; // Error as private
// number2 = y.data2; // Error as private

x.Set().Value1( 50 ); // error if you declare const X x( 5, 10 )
x.Set().Value2( 100 ); // error if you declare const X x( 5, 10 )

number1 = x.Get().Value1();
number2 = x.Get().Value2();

// x.Get().Value1( 500 ); // error if you declare const X x( 5, 10 )
// x.Get().Value2( 1000 ); // error if you declare const X x( 5, 10 )

return 0;
}
 
G

Gerhard Fiedler

Nephi said:
Possibly, later version as C++14 over C++11 should fix the problem.
The C++ Compiler should fail to compile if it detects auto's variable
definition attempts to access private class with the return type.

Example 1:
auto y = x.Get();

number1 = y.Value1(); // OK -- should fail to compile because of private
number2 = y.Value2(); // OK -- should fail to compile because of private

Example 2:
number1 = x.Get().Value1();
number2 = x.Get().Value2();

If I understand you correctly, you seem to think that the code in
example 1 should be illegal and the code in example 2 should be legal.
If so, why? I don't really see a conceptual difference between the two,
other than that the x.Get() part is essentially "cached" in the variable
y in example 1.

Gerhard
 

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

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top