C ++ std :: string и NULL const char * - PullRequest
8 голосов
/ 12 ноября 2009

Я работаю в C ++ с двумя большими частями кода, один из которых выполнен в "стиле C", а другой - в "стиле C ++".

В коде типа C есть функции, которые возвращают const char *, а в коде C ++ во многих местах есть такие вещи, как

const char* somecstylefunction();
...
std::string imacppstring = somecstylefunction();

где она строит строку из константного символа *, возвращаемого кодом стиля C.

Это работало до тех пор, пока код стиля C не изменился и не начал иногда возвращать указатели NULL. Это, конечно, вызывает ошибки сегмента.

Вокруг много кода, и поэтому я хотел бы наиболее экономным способом решить эту проблему. Ожидаемое поведение - в этом случае imacppstring будет пустой строкой. Есть ли хорошее, гладкое решение для этого?

Обновление

const char *, возвращаемый этими функциями, всегда является указателем на статические строки. Они использовались в основном для передачи информативных сообщений (предназначенных для регистрации, скорее всего) о любых неожиданных действиях в функции. Было решено, что иметь эти возвращаемые значения NULL для «ничего не сообщать» было бы хорошо, потому что тогда вы могли бы использовать возвращаемое значение в качестве условного, то есть

if (somecstylefunction()) do_something;

тогда как раньше функции возвращали статическую строку "";

Будь это хорошая идея, я не собираюсь трогать этот код, и в любом случае это не мое дело.

Чего я хотел избежать, так это отслеживания каждой инициализации строки для добавления функции-оболочки.

Ответы [ 7 ]

13 голосов
/ 12 ноября 2009

Вероятно, лучшее, что нужно сделать, - это установить функции библиотеки C в их поведение перед изменением. но, возможно, у вас нет контроля над этой библиотекой.

Второе, что нужно рассмотреть, - это изменить все случаи, когда вы зависите от функций C lib, возвращающих пустую строку, чтобы использовать функцию-обертку, которая «исправит» указатели NULL:

const char* nullToEmpty( char const* s)
{
    return (s ? s : "");
}

Так что теперь

std::string imacppstring = somecstylefunction();

может выглядеть так:

std::string imacppstring( nullToEmpty( somecstylefunction());

Если это недопустимо (это может быть много занятой работы, но это должно быть одноразовое механическое изменение), вы можете реализовать «параллельную» библиотеку, которая имеет те же имена, что и используемая в настоящее время библиотека C lib. с этими функциями просто вызывая оригинальные функции C lib и исправляя NULL-указатели в зависимости от ситуации. Вам нужно поиграть в некоторые хитрые игры с заголовками, компоновщиком и / или пространствами имен C ++, чтобы заставить это работать, и это имеет огромный потенциал, чтобы вызвать путаницу в будущем, поэтому я бы подумал, прежде чем идти по этому пути .

Но что-то вроде следующего может помочь вам начать:

// .h file for a C++ wrapper for the C Lib
namespace clib_fixer {
    const char* somecstylefunction();
}


// .cpp file for a C++ wrapper for the C Lib
namespace clib_fixer {
    const char* somecstylefunction() {
        const char* p = ::somecstylefunction();

        return (p ? p : "");
    }
}

Теперь вам просто нужно добавить этот заголовок в файлы .cpp, которые в данный момент вызывают функции C lib (и, возможно, удалить заголовок для C lib) и добавить

using namespace clib_fixer;

в файл .cpp с использованием этих функций.

Это может быть не слишком плохо. Может быть.

6 голосов
/ 12 ноября 2009

Что ж, без изменения каждого места, где C ++ std::string инициализируется непосредственно из вызова функции C (чтобы добавить проверку нулевого указателя), единственным решением было бы запретить вашим функциям C возвращать нулевые указатели.

В компиляторе GCC вы можете использовать расширение компилятора "Условные выражения с опущенными операндами", чтобы создать макрос-обертку для вашей функции C

#define somecstylefunction() (somecstylefunction() ? : "")

но в общем случае я бы советовал против этого.

3 голосов
/ 12 ноября 2009

Полагаю, вы можете просто добавить функцию-обертку, которая проверяет NULL и возвращает пустую строку std :: string. Но что более важно, почему ваши функции C теперь возвращают NULL? Что означает указатель NULL? Если это указывает на серьезную ошибку, вы можете захотеть, чтобы ваша функция-обертка вызвала исключение.

Или, для безопасности, вы можете просто проверить NULL, обработать NULL и только потом создать std :: string.

const char* s = somecstylefunction();
if (!s) explode();
std::string str(s);
2 голосов
/ 12 ноября 2009

Вы можете заключить все ваши вызовы в функции C-stlye примерно так ...

std::string makeCppString(const char* cStr)
{
    return cStr ? std::string(cStr) : std::string("");
}

Тогда, где у вас есть:

std::string imacppstring = somecstylefunction(); 

заменить его на:

std::string imacppstring = makeCppString( somecystylefunction() );

Конечно, это предполагает, что создание пустой строки является приемлемым поведением, когда ваша функция возвращает NULL.

2 голосов
/ 12 ноября 2009

Для портативного решения:

(а) определите свой собственный тип строки. Самая большая часть - это поиск и замена по всему проекту - это может быть просто, если это всегда std :: string или большая одноразовая боль. (Я бы сделал единственное требование, что он заменяется на Liskov для std :: string, но также создает пустую строку из нулевого символа *.

Самая простая реализация - это публичное наследование от std :: string. Несмотря на то, что это неодобрительно (по понятным причинам), в этом случае все будет в порядке, а также поможет сторонним библиотекам, ожидающим std::string, а также инструментам отладки. В качестве альтернативы, аггейт и вперед - гадость.

(b) #define std :: string для вашего собственного строкового типа. Рискованно, не рекомендуется. Я бы не стал этого делать, если бы не знал, что кодовые базы очень хорошо задействованы и экономит вам массу работы (и я бы добавил несколько заявлений об отказе от ответственности для защиты остатков моей репутации;))

(c) Я обошел несколько таких случаев, переопределив оскорбительный тип для некоторого служебного класса только для цели включения (поэтому #define гораздо более ограничен по объему). Тем не менее, я понятия не имею, как это сделать для char *.

(d) Написать импортную оболочку. Если заголовки библиотеки C имеют довольно регулярную разметку и / или вы знаете кого-то, кто имеет некоторый опыт разбора кода C ++, вы можете сгенерировать «заголовок-обертку».

(e) попросить владельца библиотеки сделать значение «Null string» настраиваемым хотя бы во время компиляции. (Приемлемый запрос, так как переключение на 0 может нарушить совместимость и в других сценариях) Вы можете даже предложить отправить изменение самостоятельно, если для вас это меньше работает!

1 голос
/ 12 ноября 2009

Обычно я не рекомендую создавать подклассы для стандартных контейнеров, но в этом случае это может сработать.

class mystring : public std::string
{
    // ... appropriate constructors are an exercise left to the reader
    mystring & operator=(const char * right)
    {
        if (right == NULL)
        {
            clear();
        }
        else
        {
            std::string::operator=(right);  // I think this works, didn't check it...
        }
        return *this;
    }
};
0 голосов
/ 12 ноября 2009

Нечто подобное должно решить вашу проблему.

const char *cString;
std::string imacppstring;

cString = somecstylefunction();
if (cString == NULL) {
  imacppstring = "";
} else {
  imacppstring = cString;
}

Если хотите, вы можете вставить логику проверки ошибок в ее собственную функцию. Тогда вам придется разместить этот блок кода в меньшем количестве мест.

...