C++ std:: string и NULL const char*
Я работаю на C++ с двумя большими фрагментами кода, один из которых выполнен в " стиле C "и один в" стиле C++".
код C-типа имеет функции, которые возвращают const char* , а код C++ имеет во многих местах такие вещи, как
const char* somecstylefunction();
...
std::string imacppstring = somecstylefunction();
где он строит строку из const char*, возвращаемого кодом стиля C.
это работало до тех пор, пока код стиля C не изменился и не начал иногда возвращать нулевые указатели. Это, конечно, вызывает seg тектонические нарушения.
есть много кода вокруг, и поэтому я хотел бы наиболее экономным способом исправить эту проблему. Ожидаемое поведение заключается в том, что imacppstring будет пустой строкой в этом случае. Есть ли хорошее, скользкое решение для этого?
обновление
const char*, возвращаемый этими функциями, всегда указывает на статические строки. Они использовались в основном для передачи информативных сообщений( скорее всего, предназначенных для регистрации) о любых неожиданных поведение в функции. Было решено, что иметь эти возвращаемые NULL на "nothing to report" было приятно, потому что тогда вы могли бы использовать возвращаемое значение как условное, т. е.
if (somecstylefunction()) do_something;
тогда как перед функциями возвращалась статическая строка"";
была ли это хорошая идея, я не собираюсь трогать этот код, и это не зависит от меня в любом случае.
чего я хотел избежать, это отслеживать каждую инициализацию строки, чтобы добавить функцию-оболочку.
7 ответов
вероятно, лучшая вещь, чтобы сделать, чтобы исправить функции библиотеки C, чтобы их предварительно разбив изменение поведения. но, может быть, у тебя нет контроля над библиотекой.
второе, что нужно учитывать, - это изменить все экземпляры, в которых вы зависите от функций c lib, возвращающих пустую строку, чтобы использовать функцию-оболочку, которая "исправит" нулевые указатели:
const char* nullToEmpty( char const* s)
{
return (s ? s : "");
}
теперь
std::string imacppstring = somecstylefunction();
может выглядеть так:
std::string imacppstring( nullToEmpty( somecstylefunction());
если это неприемлемо (это может быть много занятой работы, но это должно быть одноразовое механическое изменение), вы можете реализовать "параллельную" библиотеку, которая имеет те же имена, что и C lib, который вы используете в настоящее время, с этими функциями, просто вызывающими исходные функции C lib и фиксирующими нулевые указатели по мере необходимости. Вам нужно будет сыграть в некоторые сложные игры с заголовками, компоновщиком и / или пространствами имен 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-файл, использующий эти функции.
это может быть не слишком плохо. Возможно.
Ну, не меняя каждое место, где C++ std::string
инициализируется непосредственно из вызова функции C (чтобы добавить проверку нулевого указателя), единственным решением было бы запретить вашим функциям C возвращать нулевые указатели.
в компиляторе GCC вы можете использовать расширение компилятора "Conditionals with Omitted Operands" для создания макроса-оболочки для вашей функции C
#define somecstylefunction() (somecstylefunction() ? : "")
но в общем случае я бы не советовал этого делать.
Я полагаю, вы можете просто добавить функцию-оболочку, которая проверяет значение NULL и возвращает пустую строку std::. Но что более важно, почему ваши функции C теперь возвращают NULL? Что указывает нулевой указатель? Если это указывает на серьезную ошибку, вы можете захотеть, чтобы ваша функция-оболочка выдала исключение.
или, чтобы быть в безопасности, вы можете просто проверить значение NULL, обработать нулевой случай и только затем построить std::string.
const char* s = somecstylefunction();
if (!s) explode();
std::string str(s);
для портативного решения:
(a) определите свой собственный тип строки. Самая большая часть-это поиск и замена по всему проекту - это может быть просто, если это всегда std:: string или большая одноразовая боль. (Я бы сделал единственный запрос, что это Liskov-substitutable для std:: string, но также создает пустую строку из нулевого символа *.
самая простая реализация наследует публично от std:: string. Даже если это нахмурилось (для понятного причины), это было бы нормально в этом случае, а также помочь с сторонними библиотеками, ожидающими std::string
, а также инструментов отладки. В качестве альтернативы, aggegate и forward-yuck.
(b) #определите std::string как свой собственный тип строки. Рискованно, не рекомендуется. Я бы не сделал этого, если бы не знал, что кодовые базы участвуют очень хорошо и экономят вам массу работы (и я бы добавил некоторые оговорки, чтобы защитить остатки моей репутации ;))
(c) я работал вокруг нескольких таких случаев re - #define'ING наступательный тип к некоторому классу утилиты только для цели включения (поэтому #define намного более ограничен в области). Однако я понятия не имею, как это сделать для char *
.
(d) напишите оболочку импорта. Если заголовки библиотеки C имеют довольно обычный макет и / или вы знаете кого-то, кто имеет некоторый опыт синтаксического анализа кода C++, вы можете создать "заголовок оболочки".
(e) попросите владельца библиотеки сделать значение " Null string настраивается, по крайней мере, во время компиляции. (Приемлемый запрос, так как переключение на 0 может нарушить совместимость, а также в других сценариях) вы можете даже предложить отправить изменение самостоятельно, если это меньше работы для вас!
вы можете обернуть все свои вызовы функций 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.
Я обычно не выступаю за подклассы стандартных контейнеров, но в этом случае это может сработать.
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;
}
};
что-то вроде это должно решить вашу проблему.
const char *cString;
std::string imacppstring;
cString = somecstylefunction();
if (cString == NULL) {
imacppstring = "";
} else {
imacppstring = cString;
}
Если вы хотите, вы можете вставить логику проверки ошибок в свою собственную функцию. Тогда вам придется поместить этот кодовый блок в меньшее количество мест.