Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid a common bug in a large codebase?

Tags:

c++

string

Is there a way to undefine the += on strings and wstrings for chars and wchar_t?

Basically I want to avoid bugs like the following:

int age = 27;

std::wstring str = std::wstring(L"User's age is: ");
str += age;

std::string str2 = std::string("User's age is: ");
str2 += age;

The above code will add the ascii character 27 to the string instead of the number 27.

I obviously know how to fix this, but my question is: how do I produce a compiler error in this situation?

Note: You can override += on std::string and int to properly format the string, but this is not what I want to do. I want to completely disallow this operator on these operands.

like image 705
Brian R. Bondy Avatar asked Jan 05 '09 06:01

Brian R. Bondy


People also ask

How may bugs in 1000 lines of code on average )?

On average, a developer creates 70 bugs per 1000 lines of code (!) 15 bugs per 1,000 lines of code find their way to the customers. Fixing a bug takes 30 times longer than writing a line of code. 75% of a developer's time is spent on debugging (1500 hours a year!)


2 Answers

You cannot deactivate a specific function of a class (here std::basic_string) as it is it's interface that clearly (and officially) allow that manipulation. Trying to overload the operator will only mess things up.

Now, you can "wrap" std::basic_string in another class, using private inheritance or composition and then use the public interface as a proxy to the std::basic_string part, but only the functions you want to be usable.

I recommand first replacing you string types with typedefs :

namespace myapp
{
    typedef std::string String;
    typedef std::wstring UTFString;
}

Then once your application compile fine after having replaced std::string and std::wstring by myapp::String and myapp::UTFString (those are example names), you define the wrapper class somewhere :

namespace myapp
{
/** std::basic_string with limited and controlled interface.
    */
    template< class _Elem, class _Traits, class _Ax >
    class limited_string 
    {
    public:
        typedef std::basic_string< _Elem , _Traits, _Ax > _String; // this is for easier writing
        typedef limited_string< _Elem, _Traits, _Ax > _MyType; // this is for easier writing

    private:
        _String m_string; // here the real std::basic_string object that will do all the real work!

    public:

        // constructor proxies... (note that those ones are not complete, it should be exactly the same as the original std::basic_string
        // see some STL docs to get the real interface to rewrite)
        limited_string() : m_string {}
        limited_string( const _MyType& l_string ) : m_string( l_string.m_string ) {}
        limited_string( const _Elem* raw_string ) : m_string( raw_string ) {}
        //... etc...

        // operator proxies...
        _MyType& operator= ( const _MyType& l_string ) 
        {
            m_string = l_string.m_string;
        }
        // etc...
        // but we don't want the operator += with int values so we DON'T WRITE IT!

        // other function proxies...
        size_t size() const { return m_string.size(); } // simply forward the call to the real string!
        // etc...you know what i mean...

        // to work automatically with other STL algorithm and functions we add automatic conversion functions:
        operator const _Elem*() const { return m_string.c_str(); } 

        // etc..        


    };
}

...then, you simply replace those lines :

// instead of those lines...
    //typedef std::string String; 
    //typedef std::wstring UTFString;

    // use those ones
    typedef limited_string< char, std::char_traits<char>, std::allocator<char> >                String; // like std::string typedef
    typedef limited_string< wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >       UTFString; // like std::wstring typedef 

... and your example will crash :

error C2676: binary '+=' : 'myapp::UTFString' does not define this operator or a conversion to a type acceptable to the predefined operator
error C2676: binary '+=' : 'myapp::String' does not define this operator or a conversion to a type acceptable to the predefined operator

Here is the full test application code i wrote to prove that (compiled on vc9) :

#include <string>
#include <iostream>

namespace myapp
{

    /** std::basic_string with limited and controlled interface.
    */
    template< class _Elem, class _Traits, class _Ax >
    class limited_string 
    {
    public:
        typedef std::basic_string< _Elem , _Traits, _Ax > _String; // this is for easier writing
        typedef limited_string< _Elem, _Traits, _Ax > _MyType; // this is for easier writing

    private:
        _String m_string; // here the real std::basic_string object that will do all the real work!

    public:

        // constructor proxies... (note that those ones are not complete, it should be exactly the same as the original std::basic_string
        // see some STL docs to get the real interface to rewrite)
        limited_string() : m_string {}
        limited_string( const _MyType& l_string ) : m_string( l_string.m_string ) {}
        limited_string( const _Elem* raw_string ) : m_string( raw_string ) {}
        //... etc...

        // operator proxies...
        _MyType& operator= ( const _MyType& l_string ) 
        {
            m_string = l_string.m_string;
        }
        // etc...
        // but we don't want the operator += with int values so we DON'T WRITE IT!

        // other function proxies...
        size_t size() const { return m_string.size(); } // simply forward the call to the real string!
        // etc...you know what i mean...

        // to work automatically with other STL algorithm and functions we add automatic conversion functions:
        operator const _Elem*() const { return m_string.c_str(); } 

        // etc..        


    };

    // instead of those lines...
    //typedef std::string String; 
    //typedef std::wstring UTFString;

    // use those ones
    typedef limited_string< char, std::char_traits<char>, std::allocator<char> >                String; // like std::string typedef
    typedef limited_string< wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >       UTFString; // like std::wstring typedef 
}

int main()
{
    using namespace myapp;

    int age = 27;

    UTFString str = UTFString(L"User's age is: ");
    str += age; // compilation error!
    std::wcout << str << std::endl;

    String str2 = String("User's age is: ");
    str2 += age; // compilation error!
    std::cout << str2 << std::endl;

    std::cin.ignore();

    return 0;
}

I think it would cleanly resolve your problem, but you'll have to wrapp all the functions.

like image 126
Klaim Avatar answered Sep 23 '22 11:09

Klaim


Most source control systems allow you to run sanity checks against your code during checkin. So you could set up a test the performs the validation and refuses the checkin on failure:

Example:

Test Script:

#!/bin/tcsh
# Pass the file to test as the first argument.

echo "#include <string>\
void operator+=(std::string const& , int const&);\
void operator+=(std::string const& , int);"\
| cat - $1 \
| g++ -c -x c++ -  >& /dev/null


echo $status

This script fakes the addition of the two operators above (without actually altering the source). This will cause any use of the operator+ with strings and a char to fail even if the original code compiles.

NB: operator+= idea stolen from litb. Who has since deleted his example. But credit where it was due.

like image 22
Martin York Avatar answered Sep 26 '22 11:09

Martin York