2016-01-12 30 views
3

我想使用C++其采用可变数量结构与字符C风格的API *成员如下:用char *构建动态结构的更好方法?

typedef struct _FOO 
{ 
    const char * name; 
    const char * value; 
} FOO; 

void ApiFunc(FOO const* args, unsigned count); 

为了填补这个参数,我需要循环一些其他的数据,并创建FOO在飞行中的条目。什么是最优雅的方式来做到这一点?

下面的办法似乎在第一个简单的,但不工作(因为字符串实例走出去的范围和调用之前被破坏到ApiFunc()):

// Approach A: this does *not* work 
std::vector<FOO> args; 

for (...) 
{ 
    string name = ... // something which gets 
    string value = ... // calculated in the loop 
    args.push_back(FOO{name.c_str(), value.c_str()}); 
} 

ApiFunc(args.data(), args.size()); 

把字符串对象中矢量(以防止它们被破坏)无法正常工作或 - 作为投入的载体时,琴弦被复制,并与原有的仍然破坏:

// Approach B: this also does *not* work 
std::vector<string> strings; 
for (...) 
{ 
    string name = ... // something which gets 
    string value = ... // calculated in the loop 
    strings.push_back(name); 
    strings.push_back(value); 
    args.push_back(FOO{name.c_str(), value.c_str()}); 
} 

ApiFunc(args.data(), args.size()); 

我可以防止通过创建字符串对象在堆上并使用auto_ptr来保持trac其中有k个,但是有更好的方法吗?

// Approach C: this does work 
std::vector<auto_ptr<string>> strings; 
for (...) 
{ 
    string* name = new ... 
    string* value = new ... 
    strings.push_back(auto_ptr<string>(name)); 
    strings.push_back(value); 
    args.push_back(FOO{name.c_str(), value.c_str()}); 
} 

ApiFunc(args.data(), args.size()); 

虽然方法C.似乎工作,我觉得它很不明显/难以理解。任何建议如何我可以改善它?

+2

在一个不相关的纸币,不与前导下划线后跟大写字母使用符号,[那些在所有范围内保留(http://stackoverflow.com/questions/228783/what- “实现”(编译器和标准库)是“关于使用下划线的ac标识符”。 –

+1

为什么你不能a)把'char *'设置为'char [SOME_SIZE]',这样你就可以复制字符串并完成它,或者b)如果字符串的最大尺寸不能被完成,动态地分配'name'''的值? –

+0

另外,请勿使用'auto_ptr'。 –

回答

4

我很肯定std::vector<auto_ptr<T>>是不允许的标准。改为使用unique_ptr。或者,在向量中构建字符串,然后从中构建参数。

std::vector<std::pair<std::string, std::string>> strings; 
for (...) 
{ 
    const std::string name = ...; 
    const std::string value = ...; 
    strings.push_back(std::make_pair(name, value)); 
} 
// Note: This loop must be separate so that 'strings' will not reallocate and potentially 
// invalidate the pointers returned by elements in it. 
for (const auto& pair : strings) 
{ 
    args.push_back(FOO{pair.first.c_str(), pair.second.c_str()}); 
} 

ApiFunc(args.data(), args.size()); 
0

您可以使用存储在矢量字符串:

string name = ... // something which gets 
    string value = ... // calculated in the loop 
    strings.push_back(name); 
    strings.push_back(value); 
    args.push_back(FOO{strings[strings.size()-2].c_str(), strings[strings.size()-1].c_str()}); 
+0

这将打破'strings'被重新分配的时刻,它将复制/移动所有的字符串,从而使指向它们的指针无效(小字符串优化)。 –

+2

所以你需要首先保留''字符串'(这感觉很脆弱),或者首先建立'strings',然后从中建立'args'。 –

+0

另一种可能性是,如果唯一的担心是使用基于值的容器来避免清理上的删除,则使用std :: set,它是基于节点的,并且不应该通过插入使其无效。 –

1

可以使用strdup功能。

string name = ... // something which gets 
string value = ... // calculated in the loop 
args.push_back(FOO{strdup(name.c_str()), strdup(value.c_str())}); 

之后,您将不得不删除字符串。

free(foo.name); 
free(foo.value); 
+0

当然,您必须稍后再删除它们。 –

+1

这不是一个POSIX唯一的功能 – NathanOliver

+0

@NicolBolas是的,当然!我会把这个添加到我的答案中。 – ciamej

2

由于您使用的是C API接口,因此您将不得不采用C API方式进行操作。也就是说,分配堆缓冲区并在之后释放它们。这实际上是unique_ptr<T[]>效用的一个最好的例子:

//Helper function 
std::unique_ptr<char[]> cpp_strdup(const std::string &str) 
{ 
    std::unique_ptr<char[]> ret = new char[str.size() + 1]; 
    //Must copy the NUL terminator too. 
    std::copy(str.data(), str.data() + str.size() + 1, ret.get()); 
    return ret; 
} 

//In your function: 
std::vector<unique_ptr<char[]>> strings; 

for (...) 
{ 
    string name = ... // something which gets 
    strings.push_back(cpp_strdup(name)); 

    string value = ... // calculated in the loop 
    strings.push_back(cpp_strdup(value)); 

    args.push_back(FOO{strings[strings.size() - 2].get(), strings[strings.size() - 1].get()}); 
} 

通过在strings矢量使用unique_ptr代替std::string,你巧妙地避免重新分配字符串本身时strings进行再分配。

+0

您能否看到我的解决方案无法工作的原因(不涉及手动堆分配)? –

+0

@MartinBonner:没有人说这是行不通的。但它是昂贵的。我的'strings'向量版本从不重新分配包含的字符串。 –

+0

我想在确信性能之前运行基准测试。您的版本不会重新分配,因为它已经有效地丢弃了SSO - 它可以是一个重要的优化。 –