我在C ++中使用两大段代码,一个用“C风格”完成,另一个用“C ++风格”。
C类代码具有返回const char *的函数,而C ++代码在很多地方都有类似
的内容const char* somecstylefunction();
...
std::string imacppstring = somecstylefunction();
它是从C样式代码返回的const char *构造字符串。
这有效直到C样式代码更改并开始有时返回NULL指针。这当然会导致seg故障。
周围有很多代码,所以我想以最简约的方式解决这个问题。预期的行为是imacppstring在这种情况下将是空字符串。对此有一个很好的,光滑的解决方案吗?
更新
这些函数返回的const char *始终是指向静态字符串的指针。它们主要用于传递有关函数中任何意外行为的信息性消息(最有可能记录日志)。决定在“无需报告”上返回NULL是很好的,因为那时你可以使用返回值作为条件,即。
if (somecstylefunction()) do_something;
而在函数返回静态字符串“”;
之前这是否是一个好主意,我不打算触及这个代码,但这无论如何都不适合我。
我想避免的是跟踪每个字符串初始化以添加包装函数。
答案 0 :(得分:13)
可能最好的办法是将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 : "");
}
}
现在你只需要将那个头添加到当前正在调用C lib函数的.cpp文件中(并且可能删除C lib的头文件)并添加一个
using namespace clib_fixer;
使用这些函数到.cpp文件。
那可能不是太坏。也许
答案 1 :(得分:6)
好吧,没有改变C ++ std::string
直接从C函数调用(添加空指针检查)初始化的每个地方,唯一的解决方案是禁止C函数返回空指针。
在GCC编译器中,您可以使用编译器扩展“Conditionals with Omitted Operands”为C函数创建包装器宏
#define somecstylefunction() (somecstylefunction() ? : "")
但一般情况下我会反对。
答案 2 :(得分:3)
我想你可以添加一个测试NULL的包装函数,并返回一个空的std :: string。但更重要的是,为什么你的C函数现在返回NULL? NULL指针指示什么?如果它指示严重错误,您可能希望包装函数抛出异常。
或者为了安全起见,你可以检查NULL,处理NULL情况,然后只构造一个std :: string。
const char* s = somecstylefunction();
if (!s) explode();
std::string str(s);
答案 3 :(得分:2)
对于便携式解决方案:
(a)定义自己的字符串类型。最大的部分是搜索和替换整个项目 - 如果它总是std :: string,或者一次性很大的痛苦,这可能很简单。 (我唯一要求它是Liskov - 可替代std :: string,但也从null char *构造一个空字符串。
最简单的实现是从std :: string公开继承。即使这是不赞成的(出于可以理解的原因),在这种情况下也可以,并且还可以帮助第三方库期待std::string
以及调试工具。或者,aggegate和前进 - yuck。
(b)#define std :: string是你自己的字符串类型。风险,不推荐。我不会这样做,除非我非常了解代码库并为您节省大量工作(我会添加一些免责声明以保护我的声誉遗骸;)
(c)我已经解决了一些这样的情况,只是为了包含的目的,将攻击类型定义为某些实用程序类(因此#define的范围更加有限)。但是,我不知道如何为char *
做到这一点。
(d)编写导入包装器。如果C库头部具有相当规则的布局,并且/或者您知道某人具有解析C ++代码的经验,那么您可能能够生成“包装器头”。
(e)要求库所有者至少在编译时使“Null string”值可配置。 (自切换到0以来可接受的请求可能会在其他情况下破坏兼容性)如果不适合您,您甚至可以自己提交更改!
答案 4 :(得分: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时,构造空字符串是可接受的行为。
答案 5 :(得分: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;
}
};
答案 6 :(得分:0)
这样的事情可以解决你的问题。
const char *cString;
std::string imacppstring;
cString = somecstylefunction();
if (cString == NULL) {
imacppstring = "";
} else {
imacppstring = cString;
}
如果需要,可以将错误检查逻辑粘贴在自己的函数中。那么你必须把这个代码块放在更少的地方。