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.
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!)
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With