Ich habe einen std::vector<std::string>
, den ich für das Argument einer C
-Funktion verwenden muss, das char* foo
liest. Ich habe gesehenwie einen std::string
in char*
konvertieren. Als Neuling in C++
versuche ich zusammenzubringen, wie diese Konvertierung für jedes Element des Vektors durchgeführt und das char*
-Array erzeugt wird.
Ich habe mehrere eng verwandte SO - Fragen gesehen, aber die meisten scheinen Wege aufzuzeigen, in die andere Richtung zu gehen und std::vector<std::string>
zu erstellen.
Sie können std::transform
verwenden als:
std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);
Dafür müssen Sie convert()
implementieren als:
char *convert(const std::string & s)
{
char *pc = new char[s.size()+1];
std::strcpy(pc, s.c_str());
return pc;
}
Testcode:
int main() {
std::vector<std::string> vs;
vs.Push_back("std::string");
vs.Push_back("std::vector<std::string>");
vs.Push_back("char*");
vs.Push_back("std::vector<char*>");
std::vector<char*> vc;
std::transform(vs.begin(), vs.end(), std::back_inserter(vc), convert);
for ( size_t i = 0 ; i < vc.size() ; i++ )
std::cout << vc[i] << std::endl;
for ( size_t i = 0 ; i < vc.size() ; i++ )
delete [] vc[i];
}
Ausgabe:
std::string
std::vector<std::string>
char*
std::vector<char*>
Online-Demo: http://ideone.com/U6QZ5
Sie können &vc[0]
verwenden, wo immer Sie char**
benötigen.
Da wir new
verwenden, um Speicher für jeden std::string
(in convert
-Funktion) zuzuordnen, müssen wir den Speicher am Ende freigeben. Dies gibt Ihnen die Flexibilität, den Vektor vs
zu ändern. Sie können Push_back
weitere Zeichenfolgen hinzufügen, die vorhandene aus vs
löschen und vc
(d. h. vector<char*>
bleibt gültig!
Wenn Sie diese Flexibilität jedoch nicht wünschen, können Sie diese convert
-Funktion verwenden:
const char *convert(const std::string & s)
{
return s.c_str();
}
Und Sie müssen std::vector<char*>
in std::vector<const char*>
ändern.
Wenn Sie nach der Umwandlung vs
durch Einfügen neuer Zeichenfolgen oder durch Löschen der alten Zeichenketten ändern, werden alle char*
in vc
möglicherweise ungültig. Das ist ein wichtiger Punkt. Ein weiterer wichtiger Punkt ist, dass Sie delete vc[i]
nicht mehr in Ihrem Code verwenden müssen.
Am besten weisen Sie einen std::vector
von const char*
zu, der dieselbe Größe wie Ihr Vektor hat. Gehen Sie dann jedes Element des Vektors ab und rufen Sie c_str()
auf, um das String-Array abzurufen, und speichern Sie das entsprechende Element des Arrays. Dann können Sie den Zeiger auf das erste Element dieses Vektors an die betreffende Funktion übergeben.
Der Code würde so aussehen:
std::vector<const char *> cStrArray;
cStrArray.reserve(origVector.size());
for(int index = 0; index < origVector.size(); ++index)
{
cStrArray.Push_back(origVector[index].c_str());
}
//NO RESIZING OF origVector!!!!
SomeCFunction(&cStrArray[0], cStrArray.size());
Beachten Sie, dass Sie durch können nicht zulassen, dass der ursprüngliche Vektor von Strings zwischen dem Zeitpunkt, zu dem Sie den const char*
s vom std::strings
abrufen, und dem Zeitpunkt, zu dem Sie die C-Funktion aufrufen, in der Größe geändert werden.
Das sollte funktionieren:
char ** arr = new char*[vec.size()];
for(size_t i = 0; i < vec.size(); i++){
arr[i] = new char[vec[i].size() + 1];
strcpy(arr[i], vec[i].c_str());
}
BEARBEITEN:
Gehen Sie wie folgt vor, wenn Sie diese Datenstrukturen freigeben würden, vorausgesetzt, vec hat immer noch die richtige Anzahl von Elementen. Wenn Ihre C-Funktion dieses Array ändert, müssen Sie möglicherweise die Größe auf andere Weise ermitteln.
for(size_t i = 0; i < vec.size(); i++){
delete [] arr[i];
}
delete [] arr;
Nochmals bearbeiten:
Es ist möglicherweise nicht erforderlich, die Zeichenfolgen zu kopieren, wenn Ihre C-Funktion die Zeichenfolgen nicht ändert. Wenn Sie wissen können, wie Ihre Benutzeroberfläche aussieht, bin ich sicher, dass wir Ihnen eine bessere Hilfe bieten könnten.
Eine C++ 0x-Lösung, bei der garantiert garantiert ist, dass Elemente von std::string
zusammenhängend gespeichert werden:
std::vector<std::string> strings = /* from somewhere */;
int nterms = /* from somewhere */;
// using std::transform is a possibility depending on what you want
// to do with the result of the call
std::for_each(strings.begin(), string.end(), [nterms](std::string& s)
{ ModelInitialize(&s[0], nterms); }
Wenn die Funktion null ihr Argument beendet, ist (s.begin(), s.end())
nach dem Aufruf möglicherweise nicht aussagekräftig. Sie können das Problem nachbearbeiten:
s = std::string(s.begin(), std::find(s.begin(), s.end(), '\0'));
Eine aufwendigere Version, die jeden String separat in einen char[]
kopiert:
typedef std::unique_ptr<char[]> pointer;
std::vector<pointer> args;
std::transform(strings.begin(), strings.end()
, std::back_inserter(args)
, [](std::string const& s) -> pointer
{
pointer p(new char[s.size()]);
std::copy(s.begin(), s.end(), &p[0]);
return p;
});
std::for_each(args.begin(), args.end(), [nterms](pointer& p)
{ ModelInitialize(p.get(), nterms); });