在我的c++应用程序中,我使用了一个公开C API的外部库。有些C函数接受字符串数组作为输入,并使用char**
:
void c_api_function(char** symbols, int count);
(注意:我认为指向const的指针更合适,但似乎const的正确性对库作者来说并不重要。)
字符串必须使用特定的编码。
目前,为了调用API,我首先将字符串转换为正确的编码并将结果存储在vector<string>
中。然后我创建了一个可以传递给C API的vector<char*>
:
std::string encode(std::string const& symbol);
void call_api(std::vector<std::string> const& symbols)
{
std::vector<std::string> encoded_symbols;
for (auto const& s : symbols)
{
encoded_symbols.push_back(encode(s));
}
std::vector<char*> encoded_symbol_ptrs;
for (auto const& s : encoded_symbols)
{
encoded_symbols_ptrs.push_back(s.data());
}
c_api_function(encoded_symbols_ptrs.data(), (int)encoded_symbols_ptrs.size());
}
我不喜欢这种方法,因为我需要两个向量。第一个向量确保字符串保持活动状态,第二个向量可以传递给API。是否有一种方法可以只使用单个容器,但仍然使用自动内存管理?如果需要,我可以自由地更改encode
函数的签名,例如,使用std::unique_ptr
作为返回值。
//
// This is how to do this with the smallest number of allocations as possible (best performance)
//
// I guess: if string contains only ascii encode has no job to do and then input == output,
// then allocation isn't necessary and input string can be used to pass to c_api_function()
// I would recommend to change the encode() to do not generate output string if encode has nothing to do
// in that case if encode() returns true (encoding was made: input != output) and output will be store in 'out'
// if encode() returns false then input == output and 'out' isn't used
//
bool encode(const std::string& symbol_in, std::string& out);
void call_api(const std::vector<std::string>& symbol_in) {
const size_t c = symbol_in.size(); // size usually have cost in operation: end - begin :)
if (c > 0x7FFFFFFF) { // good idea if you must convert from size_t to int further
throw std::overflow_error("..we have a problem here..");
}
auto it_in = symbol_in.cbegin(); // const iterator for input
auto it_end = symbol_in.cend(); // const iterator for input end
std::vector<std::string> encoded_symbols(c); // allocate array of string, but some std::string items may not be used if encode will return false
std::vector<const char*> encoded_symbols_raw(c); // array of C raw pointers
auto it_out = encoded_symbols.begin(); // iterator for std::string objects output
auto raw_out = encoded_symbols_raw.begin(); // iterator for raw output
for (; it_in != it_end; ++it_in, ++it_out, ++raw_out) {
if (encode(*it_in, *it_out)) { // if *it_out contains encoding result:
*raw_out = it_out->c_str(); // set std::string buffer as raw pointer
}
else {
*raw_out = it_in->c_str(); // no encoding needed - just pass input string buffer
}
}
c_api_function((char**)encoded_symbols_raw.data(), (int)c);
}
因为encoded_symbols
只存在于你的call_api()
函数中,我更愿意有一个包含所有编码符号和作用于它们的成员函数的对象。作为可执行示例:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
void c_api_function(char** symbols, int count)
{
for(int x = 0; x < count; ++x)
{
std::cout << symbols[x] << 'n';
}
}
std::string encode(std::string const& symbol)
{
return symbol;
}
class EncodedSymbols
{
friend void call_api(EncodedSymbols& symbols);
friend void call_api(std::vector<std::string> const& symbols);
public:
EncodedSymbols(const EncodedSymbols& other) = delete;
EncodedSymbols(EncodedSymbols&& other) = default;
~EncodedSymbols()
{
for(int x = 0; x < number_of_encoded_symbols; ++x)
{
delete[] encoded_symbol_array[x];
}
}
static EncodedSymbols create_from(const std::vector<std::string>& symbols)
{
EncodedSymbols obj;
obj.encoded_symbol_array = std::make_unique<char*[]>(symbols.size());
obj.number_of_encoded_symbols = symbols.size();
for(int x = 0; x < symbols.size(); ++x)
{
const std::string encoded = encode(symbols[x]);
obj.encoded_symbol_array[x] = new char[encoded.length() + 1];
std::copy(encoded.begin(), encoded.end(), obj.encoded_symbol_array[x]);
obj.encoded_symbol_array[x][encoded.length()] = ' ';
}
return obj;
}
void call_api()
{
c_api_function(encoded_symbol_array.get(), number_of_encoded_symbols);
}
private:
EncodedSymbols() = default;
std::unique_ptr<char*[]> encoded_symbol_array;
int number_of_encoded_symbols;
};
void call_api(EncodedSymbols& symbols)
{
c_api_function(symbols.encoded_symbol_array.get(),
symbols.number_of_encoded_symbols);
}
void call_api(std::vector<std::string> const& symbols)
{
auto encoded = EncodedSymbols::create_from(symbols);
c_api_function(encoded.encoded_symbol_array.get(),
encoded.number_of_encoded_symbols);
}
int main()
{
std::vector<std::string> symbols{"one", "two"};
auto encoded_symbols = EncodedSymbols::create_from(symbols);
encoded_symbols.call_api();
call_api(encoded_symbols);
call_api(symbols);
return 0;
}
如果你的C库中有其他函数对编码符号起作用,那么(在我看来)把它们放在一个类中更有意义。所有的手动内存管理都可以隐藏在一个漂亮的界面后面。
如果你愿意,你也可以有一个作用于EncodedSymbols
实例的裸函数。我也包含了那个变体。
作为第三种选择,您可以保留当前的函数原型,并为RAII使用EncodedSymbols
类型。我在我的例子中也展示了这一点。