Re: Protecting against uninitialized variables
On 3/18/07 10:05 PM, in article
1174275327.899744.20370@b75g2000hsg.googlegroups.com, "Tim Conkling"
<tconkling@gmail.com> wrote:
The compiler I use (Visual C++ 2003) warns when I am reading from a
potentially uninitialized variable, unless that variable is a class
member.
struct Foo { int x; };
void foo()
{
Foo myFoo;
printf("%d", myFoo.x); // undefined output
}
(I'm not sure if there are compilers out there that warn about this
problem; mine definitely does not.)
The gcc compiler has a setting to enable such a warning.
This has caused headaches in projects I've worked on, especially since
this sort of error isn't caught in Debug builds, making it hard to
notice & track down. To protect against this sort of error, I've
created a templated class called "safe_type," and I'm looking for
comments on its design -- possible issues, whether it's useful, etc.
template <class T>
struct safe_type_traits;
template<>
struct safe_type_traits<bool>
{
static bool DefaultValue() { return false; }
};
template<>
struct safe_type_traits<int>
{
static int DefaultValue() { return 0; }
};
template<>
struct safe_type_traits<float>
{
static float DefaultValue() { return 0.0f; }
};
If each type requires its own safe_type specialization, then there is little
benefit in declaring safe_type a class template instead of three separate
classes - one for each of the target types. But the specializations are not
needed, it's possible to write one template capable of zero-initializing all
three types (and any other fundamental type).
template <class T>
struct safe_type_traits<T*>
{
static T* DefaultValue() { return NULL; }
};
template <class T>
struct safe_type
{
private:
T m_val;
public:
safe_type() : m_val(safe_type_traits<T>::DefaultValue()) {}
safe_type(T val) : m_val(val) {}
safe_type& operator=(safe_type rhs) { m_val = rhs.m_val; return
*this; }
safe_type& operator=(T rhs) { m_val = rhs; return *this; }
// conversion operator
operator T() const { return m_val; }
// operator ->
T operator->() const { return m_val; }
// operator ==
template <class U> bool operator==(U rhs) { return (m_val == rhs); }
// operator !=
template <class U> bool operator!=(U rhs) { return (m_val != rhs); }
// operator +=
template <class U> safe_type& operator+=(U rhs)
{
m_val += rhs;
return *this;
}
[...rest of implementation omitted]
In my view this solution is over-engineered - it goes far beyond what is
required to ensure the zero-initialization of a fundamental type serving as
a class member. I would think that a more pared-down implementation would do
the job:
template <class T>
struct safe_type
{
safe_type() : value(T()) {}
operator T&() { return value; }
private:
T value;
};
typedef safe_type<int> safe_int;
struct C
{
C() {}
private:
safe_int i;
};
using std::cout;
int main()
{
C c;
cout << c.i << "\n";
cout << c.i + 5 << "\n"; // safe_int may appear in expressions
}
Program Output:
0
5
Greg
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]