How to convert string to template type in C ++

I want to read and parse a text file in C ++ in a general way. A file always consists of key-value pairs, one per line. The key is the pattern as well as the meaning. I foresee the key and the values ​​will always be the main type (int, float, string).

My problem is that I do not know how to convert a string of key or value to the correct type.

I tried the following:

template<class Key, class T> inline void EventReportReader<Key, T>::validateFileFormat() { // Read file line by line and check that the first token is of type Key and the second one of type T std::string line; try { boost::regex re( "(\\S+)\\s+(.*)" ); while( getline( inStream_, line ) ) { boost::cmatch matches; if( boost::regex_match( line.c_str(), matches, re ) ) { std::cout << re << " matches " << line << std::endl; std::cout << " 1st : " << matches[1] << "\n 2nd : " << matches[2] << std::endl; // test types Key *k = dynamic_cast<Key*>(&matches[1]); T t = dynamic_cast<T>(matches[2]); } } } catch( boost::regex_error& e ) { // todo problem with regular expression, abort } } 

And the use of this method is as follows:

 // This in turn calls the method validateFileFormat EventReportReader<float, int> reader( testFileName ); 

Result

/home/vonhalle/dev/EventBasedReport/libs/event_based_report/EventReportReader.h:121:60: error: cannot dynamic_cast '(const boost :: sub_match *) matches.boost :: match_results :: operator [] with BidIIterator = const char *, Allocator = std :: allocator>, boost :: match_results :: const_reference = const boost :: sub_match & (like const const boost :: sub_match) to enter 'float (target is not a pointer or class reference) / home / vonhalle / dev / EventBasedReport / libs / event _based_report / EventReportReader.h: 122: 53: error: cannot dynamic_cast 'matches.boost :: match_results :: operator [] with BidiIterator = const char *, Allocator = std :: allocator>, boost :: match_results :: const_reference = const boost :: sub_match & (of type 'const struct boost :: sub_match) to enter' int (target is not a pointer or reference)

How can I do it? Is it possible?

EDIT: A file may look like this if the pattern is <float, int>

 1.14 5 2.34 78 0.56 24 

or this if the pattern is <int, string>

 23 asdf 45 2222 1 bbbb 

EDIT2:

The above problem is partially erroneous. A key is never a string; a value can be a string. Therefore, everything that is in front of the first space is the key, and the rest is the value. Sorry for this mistake.

+4
source share
3 answers

I think your basic approach is wrong.
It seems you are trying to use template meta programming to achieve your goals.
This is probably not a good idea.

A simpler approach is to just use C ++ streams.
These stream objects already know how to read all the basic types. And anyone who wants to do something in C ++ will add the appropriate input and output operators for the stream of their class; therefore, it is quite universal that you can read any type as a key and value (with the restriction that it must fit on one line).

So, now you just need to use the standard template logic to define an operator that will read two objects of different types on the same line.

Try the following:

 #include <string> #include <memory> #include <fstream> #include <sstream> #include <vector> #include <iterator> #include <algorithm> // These can be any types. typedef std::string Key; typedef int Value; // The data type to hold the data. template<typename K,typename V> class Data: public std::pair<K, V> { }; 

Here is the code that will read one record from one line of the file:
Note that the Data data type and this input statement are both templates and can thus prepare key / value pairs for any objects (as long as these objects know how the streams themselves are).

 template<typename K,typename V> std::istream& operator>>(std::istream& stream, Data<K,V>& data) { // Read a line into a local string. std::string line; std::getline(stream,line); // convert the line into a stream and read the key/value from the line std::stringstream linestream(line); linestream >> data.first >> data.second; // If the linestream is bad, then reading the key/value failed // If reading one more `char` from the linestream works then there is extra crap in the line // thus we have bad data on a line. // // In either case set the bad bit for the input stream. char c; if ((!linestream) || (linestream >> c)) { stream.setstate(std::ios::badbit); } // return the stream. return stream; } 

Using it just now means using a stream:

 int main() { // The input file std::ifstream file("Plop"); // We will convert the file and store it into this vector. std::vector<Data<Key,Value> > data; // Now just copy the data from the stream into the vector. std::copy(std::istream_iterator<Data<Key,Value> >(file), std::istream_iterator<Data<Key, Value> >(), std::back_inserter(data) ); } 

Note. In the above example, the key must be in one word (since it is read using a string). If you want a space as a string, you need to do extra work. But this is a question of another question.

+4
source

dynamic_cast should be used in inheritance hierarchies; you are using it wrong here.

For such a simple task, you can use threads, I think (warning, unchecked):

 template<class Key, class T> void EventReportReader<Key, T>::validateFileFormat() { std::string line; while( getline( inStream_, line ) ) { std::istringstream stream(line); Key k; T t; stream >> k >> t; if (!stream || !stream.eof()) { /*error*/ } } } 

Is there a reason why you are not actually saving the values ​​you are reading?

EDIT : specify specialization for strings

 template<class Key> void EventReportReader<Key, std::string>::validateFileFormat() { std::string line; while( getline( inStream_, line ) ) { size_t const ws = line.find(' '); if (ws == std::string::npos) { /* error */ } std::istringstream stream(line.substr(0, ws)); Key k; stream >> k; if (!stream) { /*error*/ } std::string t = line.substr(ws); boost::trim(t); } } 
+2
source

Without information, you cannot be sure if you have the right type or not. Your value can be a string or int. For example, you can analyze it using this algorithm:

  • Parse it as an int if it failed
  • Parse it as a float, if not
  • This is a string

Again, you could successfully parse the int, but you are expecting a string.


As for your implementation: you probably need a private template specification.

 template<class Key> inline void EventReportReader<Key, int>::validateFileFormat() { // readLine // parse as int.. } template<class Key> inline void EventReportReader<Key, float>::validateFileFormat() { // readLine // parse as float.. } 

But it’s probably better not to create templates for your function? If you have a β€œnormal” function, you can have the parsing logic that I described earlier.

+1
source

All Articles