Corrected: Proposal: Increasing type safety with a keyword

I

Ioannis Vranos

Alf said:
* Ioannis Vranos:

As I recall someone proposed a similar class template a while back.

But even if nobody did, I do that now, so, it's done:

template< typename T >
class Only
{
private:
T myValue;

template< typename U > Only( U v ); // No constr. from other
types.

public:
// STATIC_ASSERT( that T is built-in type or something like that )
Only( T v ): myValue( v ) {}

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

Disclaimer: above code is off-the-cuff, not fondled by any dirty compiler.

Hm, thinking of it I have actually proposed something very similar
earlier, many years ago, namely, for the purpose of ensuring 'bool'
conditional expressions in novice code.

However, that proposal was shot down by (1) others' ideas about how
unsuitable this was for novices, and (2) actually trying it, which
showed that the standard's formal UB for redefining a keyword when using
standard library headers, is not just formal UB but, with at least one
compiler, very real...


Your code above is interesting, but it doesn't work for pointers:


template< typename T >
class Only
{
private:
T myValue;

template< typename U > Only( U v ); // No constr. from other types.

public:
// STATIC_ASSERT( that T is built-in type or something like that )
Only( T v ): myValue( v ) {}

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


int main()
{
int array= {};

Only<int *> pInt= &array[0];
}
 
I

Ioannis Vranos

Corrected:

>
>
> It's not forbidden.
>
> It's just very counter-productive, especially when none of threads refer to the others.
>
> As to the "why" I've given you a link earlier, in the other group you posted to.

Just search in newsgroups at google, the word "cross-posting":

http://groups.google.gr/groups/search?hl=en&q=crossposting&qt_s=Search+Groups


I support the notion of multi-posting and not cross-posting, because at some point (including the beginning)
the discussion may become off-topic ==> for a newsgroup, while at the same time, people who consider
cross-posting "bad" and multi-posting "good" like me, post a reply only to "their" newsgroup (I don't follow
this practice myself).


so as to have a specific uptodate reference for my proposal. Why is this bad? Should I post my whole proposal
with its new additions, modifications and corrections to the thread, at every change I make?
>
> That would be a good idea for a new proposal. And then not make small changes. Versions.


Well, I have news for you. There is a new thing called the web, which is more useful than this practice. In
fact, I considered creating a wiki page for the proposal, so as people to be able to contribute easily, but I
left that idea aside so far.

An updated web page is a better reference than individual messages with the same stuff, especially for
discussions in moderated newsgroups and forums, where messages wait for approval, and other people reply to
something you have already changed.

In fact, I began this discussion thread with your recommended approach, and then it became obvious that a web
page is far better for this.

compile with g++, and I did not get a corrected reply. May you provide the messages I "ignored"?
>
> Search for my name and for Jeff Schwab in any archive of this thread.


I will do it and post replies.
 
I

Ioannis Vranos

Jeff said:
template<class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);

public:

typedef Value_type value_type;
typedef Value_type& reference;

only(value_type value)
: m_value( value ) { }

value_type value() const {
return m_value;
}
};

void example1() {
only<int> x = 4;
x = 5; /* OK */
// x = 5.0; /* Error */
// x = 5U; /* Error */
}

void example2() {
int i = 5;
only<unsigned> x = 4U;
// x = 5; /* Error */
// x = 5UL; /* Error */
// x = i; /* Error */
}

void example3() {
only<float> f = 4.0F;
// f = 4; /* Error */
// f = 5.0; /* Error */
f = 4.0F; /* OK */
}


This is interesting. Added a quick fix to behave as the stored type in all situations:

#include <iostream>


template<class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);

public:

typedef Value_type value_type;
typedef Value_type& reference;

only(value_type value)
: m_value( value ) { }

value_type value() const {
return m_value;
}


// ==> quick fix for all operations requiring to behave as the stored type
operator Value_type()
{
return m_value;
}
};


int main()
{
using namespace std;

int intArray[10]= {};

only<int *>pInt= intArray; // It works, that's nice

double doubleArray[10]= {};

only<double *>pDouble= doubleArray; // It works, that's nice

only <double *>pDouble2= pDouble; // It works, that's nice

cout<< *pDouble<< endl;
}




Why hasn't this grown up for a proposal?
 
I

Ioannis Vranos

Ioannis said:
Jeff said:
int main() {
some_class obj1 = 5; /* OK. */
some_class obj2 = obj1; /* OK. */
only<some_class> obj3 = 5; /* Compile-time error. */
only<some_class> obj4 = obj1; /* OK. */
}


This code does not compile:


john@ubuntu:~/Projects/anjuta/cpp/src$ g++ -ansi -pedantic-errors -Wall
main.cc -o foobar
main.cc: In function ‘int main()’:
main.cc:6: error: ‘only<Value_type>::eek:nly(U) [with U = int, Value_type =
some_class]’ is private
main.cc:26: error: within this context
john@ubuntu:~/Projects/anjuta/cpp/src$


Perhaps some people were right, I did not pay much attention. I repost the code from another message of mine
with a "quick fix" for some cases:

template<class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);

public:

typedef Value_type value_type;
typedef Value_type& reference;

only(value_type value)
: m_value( value ) { }

value_type value() const {
return m_value;
}


// ==> quick fix for all operations requiring to behave as the stored type
operator Value_type()
{
return m_value;
}
};


Why didn't this grow up for a proposal?
 
I

Ioannis Vranos

Jeff said:
That's not a "fix", and it won't always behave the same way as the
stored type.

That's a "quick fix", because we can define std::eek:stream & operator<<(std::eek:stream &); member function for
this,and other relevant functions/member functions for other operations.


It will work in all cases when the value is read but not modified.


Because it's trivial to write, and never strictly necessary.


I don't think it is trivial to write for all situations.
 
I

Ioannis Vranos

Corrected:


Jeff said:
> Ioannis Vranos wrote:
>
>
> That's not a "fix", and it won't always behave the same way as the stored type.

==> That's a "quick fix", because we can define std::eek:stream & operator<<(std::eek:stream &objOstream, const
only<Value_type> &) function for this,and other relevant functions/member functions for other operations.


It will work in all cases when the value is read but not modified.


>
>
> Because it's trivial to write, and never strictly necessary.


I don't think it is trivial to write for all situations.


Anyway, I added a bit more functionality. The template code as it currently is:


template <class Value_type>
class only;


template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<Value_type> &obj)
{
using namespace std;

return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }

};
 
A

Alf P. Steinbach

* Ioannis Vranos:
Your code above is interesting, but it doesn't work for pointers:


template< typename T >
class Only
{
private:
T myValue;

template< typename U > Only( U v ); // No constr. from other
types.

public:
// STATIC_ASSERT( that T is built-in type or something like that )
Only( T v ): myValue( v ) {}

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


int main()
{
int array= {};

Only<int *> pInt= &array[0];

Note that in spite of the misleading name, 'array' is not an entity that can be
indexed.


What do you mean by "doesn't work"?



Cheers & hth.,

- Alf
 
I

Ioannis Vranos

Unfortunately the following code doesn't compile:


#include <iostream>


template <class Value_type>
class only;


template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }

};



// An example of completely type-safe code:


#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only<double> result= somefunc();

only<int> i= static_cast<only<int> >(result); // ==> It fails
}



We can't initialize or assign the value of an only<double> object to an only<int> object explicitly, by using
a cast.
 
I

Ioannis Vranos

Alf said:
What do you mean by "doesn't work"?


I made a typo mistake. The code works:


template< typename T >
class Only
{
private:
T myValue;

template< typename U > Only( U v ); // No constr. from other types.

public:
// STATIC_ASSERT( that T is built-in type or something like that )
Only( T v ): myValue( v ) {}

T value() const { return myValue; }
operator T () const { return value(); } // => Unsafe solution
};


int main()
{
int array[10]= {};

Only<int *> pInt= &array[0];
}





--
Ioannis A. Vranos

C95 / C++03 Developer

http://www.cpp-software.net
 
A

Alf P. Steinbach

* Ioannis Vranos:
Unfortunately the following code doesn't compile:


#include <iostream>


template <class Value_type>
class only;


template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const
only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

template<class U> only(U);


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }

};



// An example of completely type-safe code:


#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only<double> result= somefunc();

only<int> i= static_cast<only<int> >(result); // ==> It fails
}



We can't initialize or assign the value of an only<double> object to an
only<int> object explicitly, by using a cast.

s/We/Ioannis/g

There will always be something you can wish for that some existing class doesn't
provide.

In such case, if you're unable to implement the wished for functionality it
generally doesn't mean the existing class is bad.


Cheers & hth.,

- Alf
 
I

Ioannis Vranos

Alf said:
s/We/Ioannis/g

There will always be something you can wish for that some existing class
doesn't provide.

In such case, if you're unable to implement the wished for functionality
it generally doesn't mean the existing class is bad.


Cheers & hth.,

- Alf


I am trying to port the completely compile-time type-safe code, to the use of this class:


// An example of completely type-safe code:


#include <iostream>


only double somefunc()
{
only double value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only double result= somefunc();

only int x= static_cast<int>(result);

cout<< x<< endl;

only char c= 'a';

only signed char sc= 4HH;

only unsigned char uc= 4HHU;

only signed char schar= static_cast<signed char>(c);
}


Here is what I made so far:

#include <iostream>


template <class Value_type>
class only;


// It makes the object work with cout, provided the type Value_type is
// supported by cout.
template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

/* template<class U> only(U); // No constr. from other types.*/


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }


// Makes casts, assignment and initialization to float/const float to work
operator const float() const { return static_cast<const float>(m_value); }

// Makes casts, assignment and initialization to double/const double to work
operator const double() const { return static_cast<const double>(m_value); }

// Makes casts, assignment and initialization to long double/const long double to work
operator const long double() const { return static_cast<const long double>(m_value); }

// Makes casts, assignment and initialization to char/const char to work
operator const char() const { return static_cast<const char>(m_value); }

// Makes casts, assignment and initialization to signed char/const signed char to work
operator const signed char() const { return static_cast<const signed char>(m_value); }

// Makes casts, assignment and initialization to unsigned char/const unsigned char to work
operator const unsigned char() const { return static_cast<const unsigned char>(m_value); }

// Makes casts, assignment and initialization to short/const short to work
operator const short() const { return static_cast<const short>(m_value); }

// Makes casts, assignment and initialization to unsigned short/const unsigned short to work
operator const unsigned short() const { return static_cast<const unsigned short>(m_value); }

// Makes casts, assignment and initialization to int/const int to work
operator const int() const { return static_cast<const int>(m_value); }

// Makes casts to unsigned/const unsigned to work
operator const unsigned() const { return static_cast<const unsigned>(m_value); }

// Makes casts, assignment and initialization to long/const long to work
operator const long() const { return static_cast<const long>(m_value); }

// Makes casts, assignment and initialization to unsigned long/const unsigned long to work
operator const unsigned long() const { return static_cast<const unsigned long>(m_value); }
};



// An example of completely type-safe code:

#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only<double> result= somefunc();

only<int> x= static_cast<int>(result);

cout<< x<< endl;

only<char> c= 'a';

only<signed char> sc= 4;


only<unsigned char> uc= 4;

only<signed char> schar= static_cast<signed char>(c);
}


I think this approach is promising, with the introduction of the new prefixes:

"New prefixes for signed char, unsigned char, short, unsigned short constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants.



What do you think?



--
Ioannis A. Vranos

C95 / C++03 Developer

http://www.cpp-software.net
 
I

Ioannis Vranos

More improved (definitely it needs a web page now):


#include <iostream>


template <class Value_type>
class only;


// It makes the object work with cout, provided the type Value_type is
// supported by cout.
template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

/* template<class U> only(U); // No constr. from other types.*/


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }


// Makes casts, assignment and initialization to float/const float to work
operator const only<float>() const { return static_cast<const float>(m_value); }

// Makes casts, assignment and initialization to double/const double to work
operator const only<double>() const { return static_cast<const double>(m_value); }

// Makes casts, assignment and initialization to long double/const long double to work
operator const only<long double>() const { return static_cast<const long double>(m_value); }

// Makes casts, assignment and initialization to char/const char to work
operator const only<char>() const { return static_cast<const char>(m_value); }

// Makes casts, assignment and initialization to signed char/const signed char to work
operator const only<signed char>() const { return static_cast<const signed char>(m_value); }

// Makes casts, assignment and initialization to unsigned char/const unsigned char to work
operator const only<unsigned char>() const { return static_cast<const unsigned char>(m_value); }

// Makes casts, assignment and initialization to short/const short to work
operator const only<short>() const { return static_cast<const short>(m_value); }

// Makes casts, assignment and initialization to unsigned short/const unsigned short to work
operator const only<unsigned short>() const { return static_cast<const unsigned short>(m_value); }

// Makes casts, assignment and initialization to int/const int to work
operator const only<int>() const { return static_cast<const int>(m_value); }

// Makes casts to unsigned/const unsigned to work
operator const only<unsigned>() const { return static_cast<const unsigned>(m_value); }

// Makes casts, assignment and initialization to long/const long to work
operator const only<long>() const { return static_cast<const long>(m_value); }

// Makes casts, assignment and initialization to unsigned long/const unsigned long to work
operator const only<unsigned long>() const { return static_cast<const unsigned long>(m_value); }
};



// An example of completely type-safe code:

#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;


// double result1= somefunc(); // This is an error, great!


only<double> result= somefunc();

only<int> x= static_cast<only<int> >(result);

cout<< x<< endl;

only<char> c= 'a';

only<signed char> sc= 4;


only<unsigned char> uc= 4;

only<signed char> schar= static_cast<only<signed char> >(c);
}



I think this approach is promising, with the introduction of the new prefixes:

"New prefixes for signed char, unsigned char, short, unsigned short constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants".



What do you think?
 
I

Ioannis Vranos

Compile-time type-safety proposal, based on the class of Jeff Schwab ([email protected]):


The latest code that works:


#include <iostream>


template <class Value_type>
class only;


// It makes the object work with cout, provided the type Value_type is
// supported by cout.
template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

/* template<class U> only(U); // No constr. from other types.*/


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }


// Makes casts, assignment and initialization to float/const float to work
operator const float() const { return static_cast<const float>(m_value); }

// Makes casts, assignment and initialization to double/const double to work
operator const double() const { return static_cast<const double>(m_value); }

// Makes casts, assignment and initialization to long double/const long double to work
operator const long double() const { return static_cast<const long double>(m_value); }

// Makes casts, assignment and initialization to char/const char to work
operator const char() const { return static_cast<const char>(m_value); }

// Makes casts, assignment and initialization to signed char/const signed char to work
operator const signed char() const { return static_cast<const signed char>(m_value); }

// Makes casts, assignment and initialization to unsigned char/const unsigned char to work
operator const unsigned char() const { return static_cast<const unsigned char>(m_value); }

// Makes casts, assignment and initialization to short/const short to work
operator const short() const { return static_cast<const short>(m_value); }

// Makes casts, assignment and initialization to unsigned short/const unsigned short to work
operator const unsigned short() const { return static_cast<const unsigned short>(m_value); }

// Makes casts, assignment and initialization to int/const int to work
operator const int() const { return static_cast<const int>(m_value); }

// Makes casts to unsigned/const unsigned to work
operator const unsigned() const { return static_cast<const unsigned>(m_value); }

// Makes casts, assignment and initialization to long/const long to work
operator const long() const { return static_cast<const long>(m_value); }

// Makes casts, assignment and initialization to unsigned long/const unsigned long to work
operator const unsigned long() const { return static_cast<const unsigned long>(m_value); }
};



// An example of completely type-safe code:

#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

double result1= somefunc(); // ==> It works, must be fixed.

only<double> result= somefunc();

only<int> x= static_cast<int>(result);

cout<< x<< endl;

only<char> c= 'a';

only<signed char> sc= 4;


only<unsigned char> uc= 4;

only<signed char> schar= static_cast<signed char>(c);
}



New prefixes for signed char, unsigned char, short, unsigned short constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants.
 
A

Alf P. Steinbach

* Ioannis Vranos:
I am trying to port the completely compile-time type-safe code, to the
use of this class:


// An example of completely type-safe code:


#include <iostream>


only double somefunc()
{
only double value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only double result= somefunc();

only int x= static_cast<int>(result);

cout<< x<< endl;

only char c= 'a';

only signed char sc= 4HH;

only unsigned char uc= 4HHU;

only signed char schar= static_cast<signed char>(c);
}


Here is what I made so far:

#include <iostream>


template <class Value_type>
class only;


// It makes the object work with cout, provided the type Value_type is
// supported by cout.
template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const
only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

/* template<class U> only(U); // No constr. from other types.*/


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }


// Makes casts, assignment and initialization to float/const float
to work
operator const float() const { return static_cast<const
float>(m_value); }

// Makes casts, assignment and initialization to double/const
double to work
operator const double() const { return static_cast<const
double>(m_value); }

// Makes casts, assignment and initialization to long double/const
long double to work
operator const long double() const { return static_cast<const long
double>(m_value); }

// Makes casts, assignment and initialization to char/const char to
work
operator const char() const { return static_cast<const
char>(m_value); }

// Makes casts, assignment and initialization to signed char/const
signed char to work
operator const signed char() const { return static_cast<const signed
char>(m_value); }

// Makes casts, assignment and initialization to unsigned char/const
unsigned char to work
operator const unsigned char() const { return static_cast<const
unsigned char>(m_value); }

// Makes casts, assignment and initialization to short/const short
to work
operator const short() const { return static_cast<const
short>(m_value); }

// Makes casts, assignment and initialization to unsigned
short/const unsigned short to work
operator const unsigned short() const { return static_cast<const
unsigned short>(m_value); }

// Makes casts, assignment and initialization to int/const int to work
operator const int() const { return static_cast<const int>(m_value); }

// Makes casts to unsigned/const unsigned to work
operator const unsigned() const { return static_cast<const
unsigned>(m_value); }

// Makes casts, assignment and initialization to long/const long to
work
operator const long() const { return static_cast<const
long>(m_value); }

// Makes casts, assignment and initialization to unsigned long/const
unsigned long to work
operator const unsigned long() const { return static_cast<const
unsigned long>(m_value); }
};



// An example of completely type-safe code:

#include <iostream>


only<double> somefunc()
{
only<double> value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only<double> result= somefunc();

only<int> x= static_cast<int>(result);

cout<< x<< endl;

only<char> c= 'a';

only<signed char> sc= 4;


only<unsigned char> uc= 4;

only<signed char> schar= static_cast<signed char>(c);
}


I think this approach is promising, with the introduction of the new
prefixes:

"New prefixes for signed char, unsigned char, short, unsigned short
constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants.



What do you think?

Well. I'm sorry but it seems like much complexity for little gain. Complexity
seems to stem from trying to adhere to a nebuluous specification, what the
client code /might/ want allowed and not allowed.

A better approach/design could be to leave that decision to the client code.

E.g. as in <url: http://code.google.com/p/mili/wiki/PromotionDisable>.


Cheers & hth.,

- Alf
 
B

Bart van Ingen Schenau

Regarding the second link "Safely and unsafely typed systems", I think the keyword "only", compile-time
type-safety solution accomplishes this.

More specifically I think it accomplishes in a high degree, the following:

"A third way of categorizing the type system of a programming language uses the safety of typed operations and
conversions. Computer scientists consider a language "type-safe" if it does not allow operations or
conversions which lead to erroneous conditions".

You are mistaken. That goal can not be accomplished unless you turn
the proposal completely around: Make the 'only' semantics the default
and provide special syntax for performing unsafe operations. This
would take C++ in the direction that Ada has taken.

Did you also notice that, in the table you referenced, FORTRAN and
Python are listed as type-safe languages? TTBOMK, both languages allow
implicit conversions of the style you want to disallow with the 'only'
proposal.

Bart v Ingen Schenau
 
B

Bart van Ingen Schenau

Athttp://en.wikipedia.org/wiki/Comparison_of_programming_languages, check the second table about type safety
and C++.

I have already provided some examples from the current state of the proposal, but if you do not consider them
as type-safety examples that reduce the risk of errors, ==> perhaps you should wait until the proposal gets
more complete (and covers Object Oriented programming for example), and thus more examples will be given then.
Let me rephrase my prior argumentation in very clear words:
Computer scientist will *never* consider a language type-safe unless
you have to severely bend over backwards to get 'strange' results out
of an operation.
As soon as a language has *one single* operation that can cause
'strange results', where that operation does not require any bending,
the language is by definition unsafe.

Given that fact, there is no hope left to make C++ a type-safe
language.

Additionally, type-safety is not affected by the existence of (well-
defined) implicit conversions.
The declarations
int i = 4.2;
double d = 42;
are both perfectly type-safe.

Regarding the new use of the keyword 'auto' that you ask about else-
thread, that just makes it possible to declare a variable of a type
that you as programmer can't spell out.
This new use of auto does not in any way affect type safety, because
the compiler knows exactly which type the object has and performs all
the normal type checking that it would have done if the programmer had
spelled the type out.

Bart v Ingen Schenau
 
I

Ioannis Vranos

The proposal (currently a work in progress), becomes a compile-time type-safety template class, taking the
type as an argument:



#include <iostream>


template <class ValueType>
class only;


// It makes the object work with cout, provided the type ValueType is supported by cout.
template <class ValueType>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const only<ValueType> &obj)
{
return objOstream<< obj.getValue();
}


// It converts between compatible only<T> types
template<class NewType, class OnlyType>
inline NewType type_cast(const OnlyType &obj)
{
return static_cast<NewType>(obj.getValue());
}



// Original class provided by Jeff Schwab <[email protected]>
template <class ValueType>
class only
{
ValueType localValue;

template<class U> only(U); // No constr. from other types.


public:

typedef ValueType value_type;

typedef ValueType& reference;

only(value_type value): localValue( value ) { }

const value_type &getValue() const { return localValue; }
};




// An example of completely type-safe code:


#include <iostream>


only<double> somefunc()
{
// only<double> value= 1.1F; // It produces an error, great!

only<double> value= 1.1;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only<char> c= 'a';


// It doesn't work without a cast, interesting! (because 4 is int)
only<signed char> sc= static_cast<signed char>(4);


// It doesn't work without a cast, interesting! (because 4 is int)
only<unsigned char> uc= static_cast<unsigned char>(4);


// It requires an explicit conversion between different types
only<signed char> schar= type_cast<signed char>(c);


// It doesn't work without a cast, interesting! (because 4 is int)
only<short> s= static_cast<short>(4);


// It doesn't work without a cast, interesting! (because 4 is int)
only<unsigned short> su= static_cast<unsigned short>(4);


// It doesn't work without a cast, interesting! (because 4 is int)
only<long> l= static_cast<long>(4);


// It doesn't work without a cast, interesting! (because 4 is int)
only<unsigned long> lu= static_cast<unsigned long>(4);


only<double> result= somefunc();

only<int> x= type_cast<int>(result);

cout<< x<< endl;


// double result2= somefunc(); // It produces an error, great!

// only<int> x2= result; // It produces an error, great!
}





New prefixes for signed char, unsigned char, short, unsigned short constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants.
 
N

Noah Roberts

Ioannis said:
I am trying to port the completely compile-time type-safe code, to the
use of this class:


// An example of completely type-safe code:


#include <iostream>


only double somefunc()
{
only double value= 1.0;

// Perform some operations

return value;
}


int main()
{
using namespace std;

only double result= somefunc();

only int x= static_cast<int>(result);

cout<< x<< endl;

only char c= 'a';

only signed char sc= 4HH;

only unsigned char uc= 4HHU;

only signed char schar= static_cast<signed char>(c);
}


Here is what I made so far:

#include <iostream>


template <class Value_type>
class only;


// It makes the object work with cout, provided the type Value_type is
// supported by cout.
template <class Value_type>
inline std::eek:stream & operator<<(std::eek:stream &objOstream, const
only<Value_type> &obj)
{
return objOstream<< obj.value();
}


template <class Value_type>
class only
{
Value_type m_value;

/* template<class U> only(U); // No constr. from other types.*/


public:

typedef Value_type value_type;

typedef Value_type& reference;

only(value_type value): m_value( value ) { }

value_type value() const { return m_value; }


// Makes casts, assignment and initialization to float/const float
to work
operator const float() const { return static_cast<const
float>(m_value); }

// Makes casts, assignment and initialization to double/const
double to work
operator const double() const { return static_cast<const
double>(m_value); }

// Makes casts, assignment and initialization to long double/const
long double to work
operator const long double() const { return static_cast<const long
double>(m_value); }

// Makes casts, assignment and initialization to char/const char to
work
operator const char() const { return static_cast<const
char>(m_value); }

// Makes casts, assignment and initialization to signed char/const
signed char to work
operator const signed char() const { return static_cast<const signed
char>(m_value); }

// Makes casts, assignment and initialization to unsigned char/const
unsigned char to work
operator const unsigned char() const { return static_cast<const
unsigned char>(m_value); }

// Makes casts, assignment and initialization to short/const short
to work
operator const short() const { return static_cast<const
short>(m_value); }

// Makes casts, assignment and initialization to unsigned
short/const unsigned short to work
operator const unsigned short() const { return static_cast<const
unsigned short>(m_value); }

// Makes casts, assignment and initialization to int/const int to work
operator const int() const { return static_cast<const int>(m_value); }

// Makes casts to unsigned/const unsigned to work
operator const unsigned() const { return static_cast<const
unsigned>(m_value); }

// Makes casts, assignment and initialization to long/const long to
work
operator const long() const { return static_cast<const
long>(m_value); }

// Makes casts, assignment and initialization to unsigned long/const
unsigned long to work
operator const unsigned long() const { return static_cast<const
unsigned long>(m_value); }
};

EWWW!!!! Not only is this a really long list of operators, you can't
extend it to new types (such as complex_number).

Two ways you could solve this problem:

template < typename T >
struct only {
....
template < typename Other >
explicit only( only<Other> const& other ) : value(other.value)
{
BOOST_STATIC_ASSERT((is_convertable<Other, T>::value));
}
};

only<int> x = only<int>(result);

Option two:

template < typename T >
struct only
{
...

template < typename Return >
Return cast() const
{
BOOST_STATIC_ASSERT((is_convertable< Return, T >::value));
return value;
}
};

template < typename T, typename R >
R only_cast(only<T> const& o)
{
return static_cast<T>(o); // use cast operator you've made for T.
}

only<int> x = only_cast<int>(result);

There's other ways I'm sure. You just need to analyze the problem.
I think this approach is promising, with the introduction of the new
prefixes:

"New prefixes for signed char, unsigned char, short, unsigned short
constants:

1. “HH” and “hh” for signed char constants.

2. “HHU” and “hhu” for unsigned char constants.

3. “H” and “h” for short constants.

4. “HU” and “hu” for unsigned short constants.

C++0x might let you do this:
http://en.wikipedia.org/wiki/C++0x#User-defined_literals
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top