I read a text file which contains lines each line contains data separated by delimiters like spaces or commas , I have a function which split a string to array but I want to make it a template to get different types like floats or integers beside string, I made two functions one for split to strings and the the other to floats
template<class T>
void split(const std::string &s, char delim, std::vector<T>& result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
T f = static_cast<T>(item.c_str());
result.push_back(f);
}
}
void fSplit(const std::string &s, char delim, std::vector<GLfloat>& result) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
GLfloat f = atof(item.c_str());
result.push_back(f);
}
}
the template function works fine with strings, in the other function I use atof(item.c_str()) to get the float value from string, when I use the template function with floats I get invalid cast from type 'const char*' to type 'float'
so how could I make the casting in the template function?
You cannot do:
T f = static_cast<T>(item.c_str());
In your case, you could declare a template, e.g. from_string<T>, and replace the line with:
T f = from_string<T>(item);
The you would implement it with something like:
// Header
template<typename T>
T from_string(const std::string &str);
// Implementations
template<>
int from_string(const std::string &str)
{
return std::stoi(str);
}
template<>
double from_string(const std::string &str)
{
return std::stod(str);
}
// Add implementations for all the types that you want to support...
You could use the strtof function (http://en.cppreference.com/w/cpp/string/byte/strtof)
so something like this
GLfloat f = std::strtof (item.c_str(), nullptr);
Related
I want to a program to read strings like:
integer_value 1
double_value 1.0
string_value one
I implement the following functions in order to read these:
void read_val_int(
std::vector<std::string> str_vec,
std::string str,
int& val){
if(str_vec[0]==str) val= std::stoi(str_vec[1]);
}
void read_val_dbl(
std::vector<std::string> str_vec,
std::string str,
double& val){
if(str_vec[0]==str) val= std::stoi(str_vec[1]);
}
void read_val_str(
std::vector<std::string> str_vec,
std::string str,
std::string& val){
if(str_vec[0]==str) val= str_vec[1];
}
str_vec is a vector containing two string values, e.g. {"integer_value","1"}.
str contains a string I want to compare with str_vec[0]
val is an integer, double or string that corresponds to str_vec[1] in case str_vec[0]==str is true.
I use these functions as, e.g. read_val_int(my_str_vec,"integer_value",my_int_val).
My question is: Is there a way of using one single function in order to do this? I have tried using a template but since I need to reference val this seems impossible.
Note: I'm aware of this post but it is in C and seems kinda messy to me. Maybe there is a simpler way to achieve this in C++.
If you are before C++17 and so cannot use std::variant, you can use only one function by using templates.
You declare the function as follows:
template <typename T>
void read_val(const std::string & data, T & val);
Then you specialize it for your three types:
template <>
void read_val<int>(const std::string & data, int & val)
{
val = std::stoi(data);
}
template <>
void read_val<double>(const std::string & data, double & val)
{
val = std::stod(data);
}
template <>
void read_val<std::string>(const std::string & data, std::string & val)
{
val = data;
}
And the job is done, you can use the function for you three types by calling one and only one function: read_val().
You can use it as follows:
std::string data_int("5");
std::string data_double("2.5");
std::string data_string("Hello");
int int_val;
double double_val;
std::string string_val;
read_val(data_int, int_val);
read_val(data_double, double_val);
read_val(data_string, string_val);
std::cout << int_val << std::endl;
std::cout << double_val << std::endl;
std::cout << string_val << std::endl;
As you can see, by the use of template specialization, you can use the same function for different types.
Moreover, it will automatically assure you that an allowed type is passed. Indeed, if you give something else than an int, double or std::string to the function, the compilation will fail because there is no specialization for it.
I hope it helps.
As suggested in Dave's comment, you should check the type of your variable parsing the first element of the vector.
Inside the if-else chain you can what you need with the right type of your variable.
You could also have a single function to return your values using std::variant e then printing values (or do whatever you need) using c++17 std::visit.
It could be something like this:
#include <vector>
#include <string>
#include <variant>
#include <iostream>
using my_variant = std::variant<int, double, std::string>;
my_variant read_val(
const std::vector<std::string> &str_vec)
{
if(str_vec[0]=="integer_value")
{
return std::stoi(str_vec[1]);
}
else if(str_vec[0]=="double_value")
{
return std::stod(str_vec[1]);
}
else if(str_vec[0]=="string_value")
{
return str_vec[1];
}
//notify error in some way, maybe throw
}
void print_variant(const my_variant &v)
{
std::visit([](my_variant &&var)
{
if (std::holds_alternative<int>(var))
std::cout<<"int->"<<std::get<int>(var)<<"\n";
else if (std::holds_alternative<double>(var))
std::cout<<"double->"<<std::get<double>(var)<<"\n";
else if (std::holds_alternative<std::string>(var))
std::cout<<"string->"<<std::get<std::string>(var)<<"\n";
}, v);
}
int main()
{
std::vector<std::string> vec_int {"integer_value", "1"};
std::vector<std::string> vec_dbl {"double_value", "1.5"};
std::vector<std::string> vec_str {"string_value", "str"};
print_variant(read_val(vec_int));
print_variant(read_val(vec_dbl));
print_variant(read_val(vec_str));
return 0;
}
I'm wondering if there is an elegant way of writing a single function that reads list of numbers (int or double) into a vector using a templated function?
Here is what I usually do:
template<class VecType>
vector<VecType> read_vector(const string& file){
vector<VecType> vec;
ifstream indata;
indata.open(file);
string line;
while (getline(indata, line)) {
stringstream lineStream(line);
string cell;
while (std::getline(lineStream, cell, ',')) {
vec.push_back(stod(cell));
}
}
indata.close();
return vec;
}
My problem is with the stoi or stod part. How to handle this nicely here?
What I usually do, is to use stod and let the conversion happen automatically from double to int if the VecType is int for example. But there should be much better way to do this, right?
You could have specialized template:
template <class T> T from_string(const std::string&);
template <> int from_string<int>(const std::string& s) { return stoi(s); }
template <> double from_string<double>(const std::string& s) { return stod(s); }
and use vec.push_back(from_string<VecType>(cell));
Currently I am working on a function similar to the String.Format(...) function from C#, just in C++. (String.Format(...))
But that's not my problem. The function works fine but problematic is that it takes a vector<string> as parameter and if I want to use an integer as parameter, I must write code like this:
// function prototype, the function body is not relevant here
string format(string str, vector<string> variables);
// ... some context
// i could use to_string() here,
// but imagine a complex type which only overrides the stream operator
int a = 20;
stringstream ss;
ss << a;
string a_str = format("a has the value '{}'", { ss.str() });
That's quite some boilerplate code!
Thus I need a function which converts a collection of unknown data types into a vector<string>.
I tried a few things like this:
vector<string> vec_string(vector<void*> args) {
vector <string> result;
for (unsigned i = 0; i < args.size(); i++)
{
stringstream ss;
// I can't dereference an object without knowing to pointer type. :(
ss << *((int*)args[i]);
result.push_back(ss.str());
}
return result;
}
// ... some context
int a = 10;
cout << format("some int: '{}'", vec_string({ (void*) &a }));
Which obviously only works for integer and is very uncomfortable. I feel like the only way to do this is a variadic macro but I got no idea how they work.
here is a link to my format(...) method.
I am sorry about my spelling, but I tried my best correcting it.
This can be done relatively easily with variadic templates:
template <class T>
auto toString(T&& t) {
std::stringstream s;
s << std::forward<T>(t);
return s.str();
}
template <class... T>
auto toStringVector(T&&... args) {
std::vector<std::string> res {toString(std::forward<T>(args))...};
return res;
}
This will convert each parameter to std::string via a stringstream and then return an std::vector<std::string> containing said strings. (Live example.)
You can then use this straight forward as intended in the question, that is:
std::cout << format("some text", toStringVector(any, number, of, arguments,
of, any, type));
If you are using Boost, you can skip the toString helper in favor of boost::lexical_cast:
template <class... T>
auto toStringVector(T&&... args) {
std::vector<std::string> res { boost::lexical_cast<std::string>(std::forward<T>(args))...};
return res;
}
The lexical_cast will most likely be faster on built-in types.
I figured it out, no idea how I did that on the first try - without compiler errors, but here is how I did it:
// function prototype, the function body is not relevant here
string format(string str, vector<string> variables);
template <class T>
vector<string> paramsToString(vector<string> vec, T last) {
stringstream ss;
ss << last;
vec.push_back(ss.str());
return vec;
}
template <class T, class ... REST>
vector<string> paramsToString(vector<string> vec, T next, REST ... rest) {
stringstream ss;
ss << next;
vec.push_back(ss.str());
return paramsToString(vec, rest...);
}
template <class ... ARGS>
vector<string> paramsToString(ARGS ... args) {
return paramsToString(vector<string>(), args ...);
}
// ... some context
// ComplexType overrides the stream operator.
cout << format("an int: '{0}', and string: '{1}' and some other type: '{2}'",
paramsToString(10, "Hello World", ComplexType(10)));
And it works! Even with custom types. Amazing!
Thank You guys for your help!
I have a C++ function that takes a comma separated string and splits in a std::vector<std::string>:
std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty = true) {
std::vector<std::string> result;
if (delim.empty()) {
result.push_back(s);
return result;
}
std::string::const_iterator substart = s.begin(), subend;
while (true) {
subend = std::search(substart, s.end(), delim.begin(), delim.end());
std::string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(temp);
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}
However, I would really like to be able to apply this function to mutiple datatypes. For instance, if I have the input std::string:
1,2,3,4,5,6
then I'd like the output of the function to be a vector of ints. I'm fairly new to C++, but I know there are something called template types, right? Would this be possible to create this function as a generic template? Or am I misunderstanding how template functions work?
You can declare the template function as:
template<class ReturnType>
std::vector<ReturnType> split(const std::string&, const std::string&, const bool = true);
and then specialize it for every vector type you want to allow:
template<>
std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty) {
// normal string vector implementation
}
template<>
std::vector<int> split(const std::string& s, const std::string& delim, const bool keep_empty) {
// code for converting string to int
}
// ...
You can read about string to int conversion here.
You will then need to call split as:
auto vec = split<int>("1,2,3,4", ",");
You can "templatise" this function - to start it you just need to replace std::vector<std::string> with 'std::vectorand addtemplate` before the function. But you need to take care of how to put the strings into the resulting vector. In your current implementation you just have
result.push_back(temp);
because result is vector of strings, and temp is string. In the general case though it is not possible, and if you want to use this function with e.g. vector<int> this line will not compile. However this problem is easily solved with another function - template again - which will convert string to whatever type you want to use split with. Let's call this function convert:
template<typename T> T convert(const std::string& s);
Then you need to provide specialisations of this function for any type you need. For instance:
template<> std::string convert(const std::string& s) { return s; }
template<> int convert(const std::string& s) { return std::stoi(s); }
In this way you do not need to specialise the entire function as the other answer suggests, only the part depending on the type. The same should be done for the line
result.push_back(s);
in the case without delimiters.
Your function can be generalized fairly easily to return a vector of an arbitrary type using Boost.LexicalCast. The only hiccup is this condition:
if (delim.empty()) {
result.push_back(s);
return result;
}
This only works right now because both the input and output types are std::string, but obviously cannot work if you're returning a vector containing a type other than std::string. Using boost::lexical_cast to perform such an invalid conversion will result in boost::bad_lexical_cast being thrown. So maybe you want to rethink that part, but otherwise the implementation is straightforward.
#include <boost/lexical_cast.hpp>
template<typename Result>
std::vector<Result>
split(const std::string& s, const std::string& delim, const bool keep_empty = true)
{
std::vector<Result> result;
if (delim.empty()) {
result.push_back(boost::lexical_cast<Result>(s));
return result;
}
std::string::const_iterator substart = s.begin(), subend;
while (true) {
subend = std::search(substart, s.end(), delim.begin(), delim.end());
std::string temp(substart, subend);
if (keep_empty || !temp.empty()) {
result.push_back(boost::lexical_cast<Result>(temp));
}
if (subend == s.end()) {
break;
}
substart = subend + delim.size();
}
return result;
}
Basically, all I've done is made the result type a template parameter and replaced
result.push_back(x);
with
result.push_back(boost::lexical_cast<Result>(x));
If you cannot use Boost, take a look at this answer that shows how to convert a string to some other type using a stringstream.
I'd like to be able to convert a char* to an arbitrary numeric type T
Similar to below, but without using the stringstream library, and obviously
that one uses the string type which I don't want to use either.
How would I go about doing this?
e.g.
template<class T>
T string_as_T( const string& s )
{
// Convert from a string to a T
// Type T must support >> operator
T t;
std::istringstream ist(s);
ist >> t;
return t;
}
Traits?
template<class T>
char* fmt(T value) { throw new /*some exception, or return null*/; }
char* fmt<int>(int value) { return "%d"; } // forgive rusty specialization syntax
// write fmt<double>, char, float, long, etc
template<class T>
T string_as_T( const char* s )
{
T val;
sscanf(s, fmt(val), &val);
return val;
}
You can use boost.conversion, lexical_cast to achieve this.
#include "boost/lexical_cast.hpp"
char* string = "15";
int output = 0;
output = boost::lexical_cast<INT_TYPE>(string);