Hello,
I have come back to C++ after a couple of years with Java so I am quite
rusty and this question may seem poor:
My platform is Windows XP with MSVC 7.1.
I have a class with a templatized conversion operator defined as
follows:
----------------------------------------------------------
template<class T>
class IBitImpl : public virtual IBit<T>
{
std::numeric_limits<T> m_limits;
T m_bit;
// other fns/ctor's etc
template<class C> operator C() const
throw(InvalidCastException)
{
if(std::numeric_limits<C>::digits < m_limits.digits)
{
throw InvalidCastException(L"Invalid argument -
Destination type C cannot accomodate source type T. ");
}
return (C) m_bit;
}
};
----------------------------------------------------------
So, I up and write a simple test:
----------------------------------------------------------
int x = 121234234;
IBitImpl<int> lowbit2(2);
bool isset = (1 == (x & lowbit2));
std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set?
"<<std::boolalpha<<isset<<std::endl;
----------------------------------------------------------
I get an error indicating that there is an ambiguity because in (x &
lowbit2) the conversion can be any one of (long, bool, unsigned long,
etc).
I thought that the compiler looks at x and upcasts lowbit2 to the
appropriate type (in this case int). This doesn't seem to be the case
here.
I figured the whole problem ws because of the conversion operator
defined above. Consequently, I changed the expression to make the case
explicit as follows:
---------------------------------------------------------
bool isset = (1 == (x & (int) lowbit2));
---------------------------------------------------------
which needless to say, worked fine. However, I don't understand why the
correct cast isn't automatically applied. I am missing something here
and would appreciate it if someone would point me in the right
direction.
thanks,
-vijai. 5 2876
My stupidity. The code snippet for the expression should be
bool isset = ( (x & (int) lowbit2));
However, that doesn't detract from my original question.
-vijai.
* Vijai Kalyan: I have come back to C++ after a couple of years with Java
Noted.
so I am quite rusty and this question may seem poor:
My platform is Windows XP with MSVC 7.1.
Irrelevant.
I have a class with a templatized conversion operator defined as follows:
---------------------------------------------------------- template<class T> class IBitImpl : public virtual IBit<T>
Naming: "Bit" would be a good name for this class.
Performance/design: difficult to see any advantage in having IBit<T>.
{ std::numeric_limits<T> m_limits;
Should be a 'typedef' (if anything), not a data member.
T m_bit;
Naming: is this the bit number, let's call it n, or is it the bit
value 1<<n?
Only for the latter case does it make sense to use type T.
In the test program it seems there is a member function that computes n from
m_bit, which is a bit awkward, opposite of the simplest & most efficient.
// other fns/ctor's etc
template<class C> operator C() const throw(InvalidCastException)
Javaism : avoid throw specifications in C++, except 'throw()'.
Design : don't be afraid to use 'assert'.
Types : if possible use standard exception classes or classes derived from
them, e.g. here, std::bad_cast (but: rather 'assert' instead).
{ if(std::numeric_limits<C>::digits < m_limits.digits)
See above regarding 'm_limits'.
{ throw InvalidCastException(L"Invalid argument - Destination type C cannot accomodate source type T. ");
See above regarding exception types and use of 'assert'.
} return (C) m_bit;
Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.
See above regarding naming and resulting confusion about what 'm_bit' is.
} }; ----------------------------------------------------------
So, I up and write a simple test:
---------------------------------------------------------- int x = 121234234; IBitImpl<int> lowbit2(2); bool isset = (1 == (x & lowbit2)); std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set? "<<std::boolalpha<<isset<<std::endl; ----------------------------------------------------------
I get an error indicating that there is an ambiguity because in (x & lowbit2) the conversion can be any one of (long, bool, unsigned long, etc).
Yes. Built-in operators do not constrain the types of their arguments,
except to the set of types the operator in question is defined for.
However, if you define your own '&' operator you can do that, e.g.
template< typename T, typename U >
T operator&( T a, Bit<U> const& b ){ return a & static_cast<T>( b ); }
template< typename T, typename U >
U operator&( Bit<T> const& a, U b ){ return static_cast<U>( a ) & b; }
I thought that the compiler looks at x and upcasts lowbit2 to the
'upcast' is something else entirely; here you're talking about a conversion,
in no particular direction.
appropriate type (in this case int). This doesn't seem to be the case here.
I figured the whole problem ws because of the conversion operator defined above. Consequently, I changed the expression to make the case explicit as follows:
--------------------------------------------------------- bool isset = (1 == (x & (int) lowbit2)); ---------------------------------------------------------
which needless to say, worked fine. However, I don't understand why the correct cast isn't automatically applied.
There is no correct conversion because there is no constraint whatsoever
(except to the set of types that the built-in '&' is defined for).
I am missing something here and would appreciate it if someone would point me in the right direction.
See above.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
> > ---------------------------------------------------------- template<class T> class IBitImpl : public virtual IBit<T> Naming: "Bit" would be a good name for this class. Performance/design: difficult to see any advantage in having IBit<T>.
Well, IBit is really the interface that defines the requirements for
IBitImpl.
template<class T>
class IBit
{
public:
template<class C> IBit<T>& operator=(C);
template<class C> IBit<T>& operator=(const IBit<C>&);
template<class C> operator C() const;
virtual inline T Bit() const = 0;
virtual inline T Mask() const = 0;
}; { std::numeric_limits<T> m_limits;
Your are right. A member is not called for.
Should be a 'typedef' (if anything), not a data member.
T m_bit; Naming: is this the bit number, let's call it n, or is it the bit value 1<<n?
m_bit is the bit number. There is a data member m_mask that stores the
mask.
Only for the latter case does it make sense to use type T.
Here, I don't know. I presume an 'int' or 'short' can equally hold all
possible bit numbers.
In the test program it seems there is a member function that computes n from m_bit, which is a bit awkward, opposite of the simplest & most efficient.
No. The mask is computed when the object is created and stored in
m_mask of type T.
// other fns/ctor's etc
template<class C> operator C() const throw(InvalidCastException)
Javaism : avoid throw specifications in C++, except 'throw()'. Design : don't be afraid to use 'assert'. Types : if possible use standard exception classes or classes derived from them, e.g. here, std::bad_cast (but: rather 'assert' instead).
True. I should have used bad_cast. On the other hand, most of the
standard exception classes don't seem to have a wide-string variant of
their constructors. I would rather not mix the two and prefer to use
the wide-string variant where possible. Of course, that is irrelevant
strictly speaking. I can derive form bad_cast and provide a
wide-string constructor. { if(std::numeric_limits<C>::digits < m_limits.digits)
See above regarding 'm_limits'.
{ throw InvalidCastException(L"Invalid argument - Destination type C cannot accomodate source type T. ");
See above regarding exception types and use of 'assert'.
} return (C) m_bit;
Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.
My mistake.
See above regarding naming and resulting confusion about what 'm_bit' is.
As I said, m_bit is the bit number. m_mask is the actual mask. So,
m_mask = (1 << m_bit); } }; ----------------------------------------------------------
So, I up and write a simple test:
---------------------------------------------------------- int x = 121234234; IBitImpl<int> lowbit2(2); bool isset = (1 == (x & lowbit2)); std::cout<<"Is bit "<<lowbit2.Bit()<<" in "<<x<<" set? "<<std::boolalpha<<isset<<std::endl; ----------------------------------------------------------
I get an error indicating that there is an ambiguity because in (x & lowbit2) the conversion can be any one of (long, bool, unsigned long, etc).
Yes. Built-in operators do not constrain the types of their arguments, except to the set of types the operator in question is defined for. However, if you define your own '&' operator you can do that, e.g.
template< typename T, typename U > T operator&( T a, Bit<U> const& b ){ return a & static_cast<T>( b ); }
template< typename T, typename U > U operator&( Bit<T> const& a, U b ){ return static_cast<U>( a ) & b; }
I thought that the compiler looks at x and upcasts lowbit2 to the
'upcast' is something else entirely; here you're talking about a conversion, in no particular direction.
Ah, I see. Ok. Well, I thought about defining those operators and then
started wondering why the conversion operator wouldn't work in general.
Of course, that means I would have to define a | operator as well. appropriate type (in this case int). This doesn't seem to be the case here.
I figured the whole problem ws because of the conversion operator defined above. Consequently, I changed the expression to make the case explicit as follows:
--------------------------------------------------------- bool isset = (1 == (x & (int) lowbit2)); ---------------------------------------------------------
which needless to say, worked fine. However, I don't understand why the correct cast isn't automatically applied.
There is no correct conversion because there is no constraint whatsoever (except to the set of types that the built-in '&' is defined for).
I am missing something here and would appreciate it if someone would point me in the right direction.
See above.
Great! Thanks a lot for the clarifications.
-vijai.
* Vijai Kalyan: } return (C) m_bit;
Javaism (and also C-ism): don't use C-style casts. Use e.g. static_cast.
My mistake.
See above regarding naming and resulting confusion about what 'm_bit' is.
As I said, m_bit is the bit number. m_mask is the actual mask. So,
m_mask = (1 << m_bit);
In that case, in the test program's bool isset = (1 == (x & lowbit2));
the sub-expression (I've added the necessary static_cast)
x & static_cast<int>( lowbit2 )
will not give correct result, because x isn't and'ed with a mask but with a
bitnumber.
This particular bug would probably have been avoided with more descriptive
naming... ;-)
There's also another bug in that expression, namely, the comparision with 1.
To avoid silly warnings from the compiler you can
A) compare the '&' result with the mask, or
B) compare the '&' result with 0, or
C) apply the negation operator '!' twice to the '&' result.
To avoid such bugs in general, try to design classes where it requires great
effort to use them incorrectly -- e.g., take control of all relevant ops.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
As a matter of fact, it is kind of funny.
The conversion operator actually returns the "mask" instead of the
"bit". So,
(x & lowbit2)
would work.
But that is not really why I replied.
Considering that in the above expression, an explicit cast is required
to use the mask writting the correct expression as:
x & (int) lowbit2
really enforces a certain amount of safety primarily because the
conversion operator applied checks to make sure that the destination
type (in this case int) can actually hold the mask. I would think that
is actually a "good side-effect" of the conversion operator although I
am not too sure.
For example, the following would most definitely throw a runtime
exception "bad_cast"
char x = 'a';
x & (char) lowbit2.
Likewise, so would the following:
IBitImpl<int> lowbiti(2);
IBitImpl<char> lowbitc = lowbiti;
This of course not being due to the conversion operator but due to the
templatized constructor:
template<typename C> IBitImpl(const IBitImpl<C>&)
which also performs the check that C can hold T.
However, the following wouldn't:
IBitImpl<char> lowbitc(2);
IBitImpl<int> lowbiti = lowbitc;
since an integer mask can certainly accomodate a char mask.
Interesting no?
-vijai. This discussion thread is closed Replies have been disabled for this discussion. Similar topics
21 posts
views
Thread by Makhno |
last post: by
|
15 posts
views
Thread by iuweriur |
last post: by
|
3 posts
views
Thread by BigMan |
last post: by
|
3 posts
views
Thread by Kevin Ruland |
last post: by
|
3 posts
views
Thread by CoolPint |
last post: by
|
6 posts
views
Thread by YUY0x7 |
last post: by
|
3 posts
views
Thread by Chris |
last post: by
|
12 posts
views
Thread by Jim Langston |
last post: by
|
3 posts
views
Thread by Hamilton Woods |
last post: by
| | | | | | | | | | |