Invalid conversion from `const Option* const' to `Option*'
-
Hallo zusammen,
ich fang gerade an, mich in C++ einzuarbeiten und hab direkt das erste unlösbare Problem
Meinen Kenntnissstand von Programmierung allgemein würde ich als hoch einschätzen, bezüglich C++ bin ich jedoch maximal fortgeschrittener Anfänger.Ich habe eine Klasse entwickelt, welche eine per Parameter bestimmte Textdatei lädt und daraus Optionen ausliest.
Pro Zeile steht eine Anweisung im Stil option = wert.
Zeilen, die mit einer Raute auskommentiert sind, werden dabei genau wie Leerzeilen übersprungen.Der Zugriff soll lesend mittels [] erfolgen, also z.B. durch config["mysql_user"]. Da die Konfiguration nicht im Programm verändert wird, sondern nur durch ändern der Textdatei erfolgt, dachte ich mir das Configobjekt global konstant zu deklarieren um von überall Zugriff zu haben. Leider krieg ich die folgende Fehlermeldung:
E:/cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_iterator.h: In constructor
\_\_gnu\_cxx::\_\_normal\_iterator<\_Iterator, \_Container>::\_\_normal\_iterator(const \_\_gnu\_cxx::\_\_normal\_iterator<\_Iter, \_Container>&) [with \_Iter = const Option*, \_Iterator = Option*, \_Container = std::vector<Option, std::allocator<Option> >]': ..\\integrator\\Config.cpp:55: instantiated from here E:/cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/stl_iterator.h:609: error: invalid conversion from \const Option* const' to `Option*'
Build error occurred, build is stoppedDer Code sieht wie folgt aus:
Config.h
#if !defined(_CONFIG_H) #define _CONFIG_H #include <string> #include <vector> struct Option { std::string identifier, value; inline Option(): identifier(), value() { } inline Option(std::string id): identifier(id), value() { } inline Option(std::string id, std::string val): identifier(id), value(val) { } inline bool operator ==(const Option &opt) const { return (this->identifier == opt.identifier); } }; class Config { std::vector<Option> opts; public: std::string operator [](const std::string &id) const; Config(std::string fname); }; #endif //_CONFIG_HConfig.cpp
#include <fstream> #include "Config.h" using namespace std; Config::Config(string fname) { ifstream f; string buf; // Variablenname, Wert char c; opts = vector<Option>(10); opts[0] = Option("log_file", "log.txt"); opts[1] = Option("error_file", "error.txt"); opts[2] = Option("manual_file", "man.pdf"); opts[3] = Option("mail_dir", "~/mails/"); opts[4] = Option("tpl_dir", "~/tpl/"); opts[5] = Option("mysql_user", "root"); opts[6] = Option("mysql_pw", ""); opts[7] = Option("mysql_host", "localhost"); opts[8] = Option("mysql_port", "3306"); opts[9] = Option("mysql_db", "default"); std::vector<Option>::iterator i = opts.end(); f.open(fname, ios::in); buf = ""; while (!f.eof()) { switch (c = f.get()) { case '#': while ( !f.eof() && (c = f.get()) != '\n'); if (f.eof()) break; case '\n': if (buf != "") { i->value = buf; } buf = ""; break; case '=': i = find(opts.begin(), opts.end(), buf); if (i == opts.end()) while (!f.eof() && f.get() != '\n'); buf = ""; break; case ' ': break; default: buf += c; } } } string Config::operator [](const string &id) const { std::vector<Option>::iterator i = find(opts.begin(), opts.end(), id); return (i != opts.end()) ? i->value : ""; }Main.cpp
#include <iostream> #include <string> using namespace std; extern const Config config("../config.txt"); int main(int argc, char *argv[]) { std::cout << "Hello World - " << config["mysql_user"]; };
-
Das Problem ist, dass die Methode
string Config::operator [](const string &id) constconst ist. Dadurch ist "this" const und alle Mitglieder von this auch. In dem Fall Dein vector opts. Du versuchst einen modifizierbaren Iterator von diesem vector zu bekommen. Du musst hier einen const_iterator nehmen.
string Config::operator [](const string &id) const { std::vector<Option>::const_iterator i = find(opts.begin(), opts.end(), id); return (i != opts.end()) ? i->value : ""; }
-
Das ganze hättest Du auch Zeilenweise einlesen und die Schlüssel und Werte in eine map speichern können:
#include <iostream> #include <string> #include <map> #include <fstream> #include <sstream> using namespace std; int main(int argc, char* argv[]) { map<string, string> config; string configfile = "config.txt"; ifstream in(configfile.c_str()); if(in.is_open()) { string line; while(getline(in, line)) { if(line.empty()) // Leerzeile continue; stringstream ss; string key, value, op; ss << line; ss >> key >> op >> value; if(key[0] == '#') //Kommentar continue; if(op == "=") // Wir haben eine Zuweisung config.insert(pair<string, string>(key, value)); } in.close(); } string key = "mysql_user"; cout << key << ": " << config["mysql_user"] << endl; cin.get(); return 0; }
-
Danke, werd's morgen direkt probieren, wenn ich wieder an meinem Rechner bin.
Das mit der map hab ich auch festgestellt, da hatte ich aber schon den Großteil fertiggeschrieben gehabt und hatte dann keine Lust es zu ändern ^^
ss << line; ss >> key >> op >> value; if(key[0] == '#') //Kommentar continue; if(op == "=") // Wir haben eine Zuweisung config.insert(pair<string, string>(key, value));Die Zuweisung auf die drei Strings erscheint mir hochgradig nichtdeterministisch, ist das denn effizient, das so zu lösen?
Ansonsten sieht das um einiges eleganter, unkryptischer als mein kleiner Algo aus...
EDIT:
Ah, jetzt bin ich dahinter gestiegen. Der >> Operator schneidet automatisch an den whitespaces ab, oder?
Aber in dem Fall ist sowas hier nicht möglich:
mysql_user=theDude
-
Ja hast recht.
Hier könnte eine Trim-Funktion für strings helfen#include <iostream> #include <string> #include <map> #include <fstream> #include <sstream> using namespace std; // von CodeProject void trim(string& str) { string::size_type pos = str.find_last_not_of(' '); if(pos != string::npos) { str.erase(pos + 1); pos = str.find_first_not_of(' '); if(pos != string::npos) str.erase(0, pos); } else str.erase(str.begin(), str.end()); } int main(int argc, char* argv[]) { map<string, string> config; string configfile = "config.txt"; ifstream in(configfile.c_str()); if(in.is_open()) { string line; while(getline(in, line)) { if(line.empty()) // Leerzeile continue; if(line[0] == '#') //Kommentar continue; string::size_type pos = line.find("="); if(pos == string::npos) continue; string key = line.substr(0, pos), value = line.substr(pos+1); trim(key); trim(value); config.insert(pair<string, string>(key, value)); } in.close(); } for(map<string, string>::iterator it = config.begin(); it != config.end(); ++it) cout << (*it).first << ": " << (*it).second << endl; cin.get(); return 0; }