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;
}

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