2008-10-10 7 views
24

Ich verwende oft die Execv() Funktion in C++, aber wenn einige der Argumente in C++ - Zeichenfolgen sind. Es ärgert mich, dass ich das nicht tun kann:Execv() und Const-Ness

const char *args[4]; 

args[0] = "/usr/bin/whatever"; 
args[1] = filename.c_str(); 
args[2] = someparameter.c_str(); 
args[3] = 0; 

execv(args[0], args); 

Diese nicht kompilieren, weil execv() const char * nimmt argv [], die nicht kompatibel mit const char *, also muss ich meine std kopieren: : Strings zu Zeichenarrays mit strdup(), was ein Schmerz ist.

Kennt jemand den Grund dafür?

+0

Ich denke, Sie können const_cast sicher verwenden, wenn Sie sicher sind, dass das Programm, das Sie ausführen, seine Argumente nicht ändert. Implementierung von execv() nicht. – Vanuan

+0

Ich habe C++ - Wrapper für jeden Systemaufruf geschrieben, den ich mache, so dass der Systemaufruf Ausnahmen auslöst, anstatt dass ich die Rückgabewerte überprüfen muss. Als Teil davon habe ich gerade eine Reihe von Überladungen gemacht, die alle "const_cast" für mich machen, so dass ich sie nicht über den ganzen Code streuen lassen muss. Sie brauchen Definitionen von execve, damit dies funktioniert, weil für beide Argumente const/non-const gilt. Einer ist natürlich die Systemdefinition. Die anderen sind nur Inline-Funktionen, die zu den gegebenen Casts weitergeleitet werden. – Omnifarious

Antwort

28

Die Open-Group-Basisspezifikationen erklären, warum dies so ist: Aus Gründen der Kompatibilität mit vorhandenem C-Code. Weder die Zeiger noch der String-Inhalt selbst sollen jedoch verändert werden. In diesem Fall können Sie also das const_cast-Ergebnis von c_str() erreichen.

Quote:

Die Aussage über argv[] und envp[] wird Konstanten explizit auf zukünftige Schriftsteller Sprachbindungen zu machen enthalten, die diese Objekte vollständig konstant sind. Aufgrund einer Beschränkung des ISO-C-Standards ist es nicht möglich, diese Idee in Standard C anzugeben. Die Angabe von zwei Ebenen von const - Qualifikation für die argv[] und envp[] Parameter für die exec-Funktionen könnte die natürliche Wahl sein, angesichts dieser Funktionen ändern weder das Array von Zeigern noch die Zeichen, auf die die Funktion verweist, aber dies würde den vorhandenen korrekten Code nicht zulassen. Stattdessen wird nur das Array von Zeigern als Konstante notiert.

Die Tabelle und der Text danach ist noch aufschlussreicher. Stack Overflow lässt jedoch nicht zu, dass Tabellen eingefügt werden. Daher sollte das obige Zitat ausreichen, um nach dem richtigen Ort im verknüpften Dokument zu suchen.

+0

Ich werde diese Antwort akzeptieren, obwohl Jonathans ähnlich ist. Dieser ist meiner Meinung nach besser formuliert. –

+0

Danke! Ich fügte nur ein bisschen hinzu, dass Leute weiterlesen sollten, in dem zitierten Artikel; es erklärt die Begründung noch besser, und wie, wäre es nicht für die Kompatibilität, wäre char const const [] viel vorzuziehen gewesen. –

+0

Wy ändern Sie nicht nur die Deklaration mit #ifdef __cplusplus ... – user877329

2

const ist eine C++ - Sache - execv hat char * Argumente genommen, bevor C++ existierte.

Sie können const_cast anstelle des Kopierens verwenden, da execv seine Argumente nicht ändert. Sie könnten erwägen, einen Wrapper zu schreiben, um sich das Tippen zu sparen.

Eigentlich ein größeres Problem mit Ihrem Code ist, dass Sie ein Array von Zeichen anstelle eines Arrays von Zeichenfolgen deklariert.

Versuchen: const char * args [4];

+0

Oh, ich wollte const Char * eingeben, entweder habe ich es falsch eingegeben oder der Stack-Überlauf hat meinen Asterisk chomped. Ich werde die Frage bearbeiten. –

+0

execv wird const char * arg [] nicht akzeptieren - zumindest nicht mit g ++, aber danke für die Antwort, ich habe erwogen, in non-const zu casten, aber ich wusste nicht, ob das sicher war. –

+0

Ein C-Compiler erlaubt es Ihnen, ein const char * an execv zu übergeben, aber es wird eine Warnung ausgegeben. Für einen C++ - Compiler müssen Sie jedoch const_cast verwenden. –

2

Ich habe in der Regel gehackt dies mit:

#define execve xexecve 
#include <...> 
#include <...> 
#include <...> 
#undef execve 

// in case of c++ 
extern "C" { 
    int execve(const char * filename, char ** argvs, char * const * envp); 
} 

;/

+0

Wirklich netter Hack – user877329

+0

Yuk, nicht nett. Es empfiehlt sich, die richtigen Header des Systems zu verwenden (um sicherzustellen, dass der Typ der Funktion, die der Linker einliest, genau das ist, was Sie erwarten, vorausgesetzt, die Lib und die Header stimmen überein). Verwenden Sie ein 'const_cast <>', um dies sauberer zu tun, anstatt alles zu überschreiben, indem Sie auf schreckliche Weise mit dem Präprozessor neu definiert werden. –

1

Dies ist nur eine Situation, in der C/C++ Stil const nicht sehr gut funktioniert. In Wirklichkeit wird der Kernel die an exec() übergebenen Argumente nicht ändern. Es wird nur kopiert, wenn es einen neuen Prozess erstellt. Aber das Typensystem ist nicht expressiv genug, um wirklich gut damit umzugehen.

Viele Leute auf dieser Seite schlagen vor, dass exec "char **" oder "const char * const []" nimmt. Aber keiner von denen funktioniert tatsächlich für Ihr ursprüngliches Beispiel. "char **" bedeutet, dass alles änderbar ist (sicherlich nicht für die String-Konstante "/ usr/bin/whatever"). "const char * const []" bedeutet, dass nichts veränderbar ist.Aber dann können Sie den Elementen des Arrays keine Werte zuweisen, da das Array selbst dann const ist.

Das Beste, was Sie tun können, ist eine Kompilierung-C konstant wie dieses:

const char * const args[] = { 
    "/usr/bin/whatever", 
    filename.c_str(), 
    someparameter.c_str(), 
    0}; 

Dies wird tatsächlich mit der vorgeschlagenen Art Signatur „const char * const []“ arbeiten. Was aber, wenn Sie eine variable Anzahl von Argumenten benötigen? Dann können Sie keine Kompilierzeitkonstante haben, aber Sie benötigen ein veränderbares Array. Also bist du wieder da, um Dinge zu manipulieren. Das ist der wahre Grund, warum die Typ-Signatur von exec "const char **" für Argumente annimmt.

Die Probleme sind die gleichen in C++ übrigens. Sie können einen std :: vector < std :: string> nicht an eine Funktion übergeben, die einen std :: vector < const std :: string> benötigt. Sie müssen den gesamten std :: vector schreiben oder kopieren.

Verwandte Themen