Как избежать распространенной ошибки в большой кодовой базе? - PullRequest
6 голосов
/ 05 января 2009

Есть ли способ отменить + + для строк и строк для символов и wchar_t?

В основном я хочу избежать ошибок, таких как:

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;

Приведенный выше код добавит в строку символ ascii 27 вместо числа 27.

Я, очевидно, знаю, как это исправить, но мой вопрос: как я могу вызвать ошибку компилятора в этой ситуации?

Примечание. Вы можете переопределить + = для std :: string и int, чтобы правильно отформатировать строку, но я не хочу этого делать. Я хочу полностью запретить этот оператор для этих операндов.

Ответы [ 4 ]

9 голосов
/ 05 января 2009

Вы не можете деактивировать определенную функцию класса (здесь std :: basic_string), поскольку именно его интерфейс явно (и официально) разрешает эту манипуляцию. Попытка перегрузить оператора только испортит ситуацию.

Теперь вы можете "обернуть" std :: basic_string в другой класс , используя частное наследование или композицию, а затем использовать открытый интерфейс в качестве прокси для части std :: basic_string, но только для функций Вы хотите быть пригодными для использования.

Я рекомендую сначала заменить строковые типы на typedefs:

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

Затем, после того как ваше приложение скомпилируется нормально после замены std :: string и std :: wstring на myapp :: String и myapp :: UTFString (это примеры имен), вы определяете класс оболочки где-то:

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 

... и ваш пример потерпит крах:

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

Вот полный код тестового приложения, которое я написал, чтобы доказать это (скомпилировано на 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;
}

Я думаю, это решит вашу проблему, но вам придется обернуть все функции.

8 голосов
/ 05 января 2009

Большинство систем контроля версий позволяют вам проверять работоспособность вашего кода во время регистрации. Таким образом, вы можете настроить тест, который выполняет проверку и отказывает в регистрации при ошибке:

Пример:

Тестовый скрипт:

#!/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

Этот скрипт подделывает добавление двух вышеприведенных операторов (без фактического изменения источника). Это приведет к сбою любого использования оператора + со строками и символом, даже если исходный код компилируется.

NB: оператор + = идея, украденная у litb. Кто с тех пор удалил его пример. Но кредит, где это должно было быть.

3 голосов
/ 05 января 2009

Нет простого способа предотвратить это, но есть простой способ найти его. Напишите небольшую программу, которая использует этот оператор, затем посмотрите на искаженный символ для оператора + =, который вы хотите запретить. Этот символ является уникальной строкой. В качестве части ваших автоматических тестов используйте DUMPBIN (или эквивалентный инструмент Linux / Mac), чтобы проверить, присутствует ли этот искаженный символ в ваших объектных файлах.

3 голосов
/ 05 января 2009

1) Создайте собственный строковый класс, который наследует / содержит std::string.

2) В этом классе перегрузите operator+=(int val) и сделайте его приватным.

3) Используйте этот класс для всех ваших строковых операций.

Это будет приводить к ошибкам флага компилятора, когда вы делаете что-то вроде этого:

MyString str;
str += 27;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...