2016-01-12 28 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 - 它可以是一個重要的優化。 –