C ++ std :: string和NULL const char *

时间:2009-11-12 17:59:41

标签: c++ string null

我在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;

而在函数返回静态字符串“”;

之前

这是否是一个好主意,我不打算触及这个代码,但这无论如何都不适合我。

我想避免的是跟踪每个字符串初始化以添加包装函数。

7 个答案:

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

如果需要,可以将错误检查逻辑粘贴在自己的函数中。那么你必须把这个代码块放在更少的地方。