2009-11-12 2 views
8

Я работаю на C++ с двумя большими фрагментами кода, один из которых выполнен в стиле «C» и один в стиле «C++».C++ std :: string и NULL const char *

код C-типа имеет функции, которые возвращают константный символ * и код C++ имеет во многих местах таких вещей, как

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

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

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

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

Update

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

if (somecstylefunction()) do_something; 

, тогда как раньше функция вернула статическую строку «»;

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

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

+0

Я бы упомянул, что вы «поддерживающий», как вы говорили о нем в комментарии, может нарушить некоторые довольно важные правила стиля. Я не знаю, почему вы хотите вернуть указатель на char, если он не передается в функцию через указатель. Не могли бы вы рассказать об этом? У вас могут быть большие проблемы. –

+1

Может ли кто-нибудь ответить, почему реализация std :: basic_string не обрабатывает указатель null value_type как пустую строку? – jmucchiello

+0

@jmucchiello, я думаю, потому что в принципе «пустая строка» сильно отличается от указателя, который указывает на адрес памяти 0. Пустая строка '' '' фактически занимает память как единственный пустой символ. –

ответ

13

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

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

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

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

std::string imacppstring = somecstylefunction(); 

может выглядеть следующим образом:

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

Если это неприемлемо (я t может быть много занятой работы, но это должно быть одноразовое механическое изменение), вы можете реализовать «параллельную» библиотеку с теми же именами, что и 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 файлы, которые в настоящее время требующие вызова функции Lib C (и, возможно, удалить заголовок для Lib C) и добавить

using namespace clib_fixer; 

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

Это может быть не плохой. Может быть.

+0

"и, вероятно, удалите заголовок для C lib" - я уверен, что вам нужно его удалить. Я думаю, что иначе вызов 'somecstylefunction' будет неоднозначным между' namespace clib_fixer' и 'namespace ::'. –

3

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

Чтобы быть в безопасности, вы можете просто проверить NULL, обработать случай NULL и только затем построить std :: string.

const char* s = somecstylefunction(); 
if (!s) explode(); 
std::string str(s); 
+0

В этом случае он не указывает на ошибку. Составитель решил, что чистым для «нет сообщения» должно быть указано нулевой указатель, а не указатель на статический «». Что не является необоснованным, но нарушает этот другой код. –

+0

В этом случае вы можете просто использовать встроенную функцию-обертку, которая проверяет значение NULL и возвращает пустую строку std ::, если указатель имеет значение NULL. –

0

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

const char *cString; 
std::string imacppstring; 

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

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

6

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

В GCC компилятором, вы можете использовать расширение компилятор «с опущена условных Операнды», чтобы создать оболочку макрос для функции C

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

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

+0

Да, похоже, что-то, с чем я хочу избавиться. Вероятно, лучше исправить источник проблемы, чем симптомы. –

+0

Некоторая форма макроса или набор ваших собственных оберток вокруг библиотеки будет хорошим способом решить проблему без изменения исходной библиотеки. – Anton

+1

@antonmarkov: Да, но комбинация расширения макросов trickery * и * компилятора слишком много, на мой взгляд. – AnT

1

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

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

Вы все еще думаете, что это хорошая идея? – Walter

+0

@ Уолтер, я никогда не говорил, что это была * хорошая * идея, просто способ сделать лучшее из плохой ситуации. И это работает: http://ideone.com/2xGd1A –

+0

С C++ 11, чтобы семантика перемещения могла работать между 'std :: string' и' mystring' (в любом направлении), остальные члены (кроме конструкторы) мне нужно перегружать? Мне не нужно преобразование rvalue в 'std :: string &&'? – Walter

2

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

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

Простейшая реализация наследуется публично из std :: string. (по понятным причинам), было бы хорошо в этом случае, а также помогло бы сторонним библиотекам ожидать std::string, а также инструменты отладки. В качестве альтернативы, aggegate и forward - yuck.

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

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

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

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

2

Вы можете обернуть все вызовы функций 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.

+0

'std :: string imacppstring = makeCppString (somecystylefunction);' не будет работать, потому что вы передаете адрес функции, а не ее возвращаемый 'char const *'. –

Смежные вопросы