There are millions of answers about strings, but I cannot find one that fits my problem. I tried finding it in cppreference.com, but I cannot find a solution for this specific problem.
I know how to solve the problem in general - it works. But I want to learn more C++ and find a "proper" (efficient) solution. Maybe one of the C++ wizards can help me out :)
I have a struct that contains a double and a string (the name of a variable, for example). When I want to initialize the struct through the constructor, I want to make sure that the string in the struct has a fixed length. If it is too long, I want to truncate it, if it is too short, I want to pad it. I can use resize() for that.
I am searching for a way to efficiently do that in one step in the initializer list of the constructor, if possible. Is there some clever operation I am missing?
All I can come up with is to write it in the function body of the constructor:
#include <iostream>
#include <string>
using namespace std;
const uint STRING_LENGTH = 25;
struct parameter {
double value;
string name;
parameter(double _value, const string& _name)
: value(_value) // easy
{
string name_temp(_name);
name_temp.resize(STRING_LENGTH, ' ');
name = name_temp;
};
};
int main(int argc, char* argv[]) {
parameter A(0.5, "my_variable");
parameter B(-0.1, "my other variable with the name that is too long and has to be truncated!");
cout << "parameter name: " << A.name << "\tis:\t" << A.value << '\n';
cout << "parameter name: " << B.name << "\tis:\t" << B.value << '\n';
}
I like the resize trick, but removing the const& (so that the caller can std::move() the string in if they want to) might be a slightly better interface. Here there will be at most one copy in the actual function body (assuming the caller moved the string in), whereas in the original version there were three in the worst case.
parameter(double _value, string _name)
: value(_value), name(std::move(_name))
{
name.resize(STRING_LENGTH, ' ');
};
If you really want to handle this in the constructor's initialization list, write a helper function that it can call, eg:
std::string&& fix_length(std::string &&s) {
s.resize(STRING_LENGTH, ' ');
return std::move(s);
}
struct parameter {
double value;
std::string name;
parameter(double _value, std::string _name)
: value(_value), name(fix_length(std::move(_name)))
{
}
};
Online Demo
However, if you really need a fixed-length string, consider using std::array instead, eg:
class fixedString
{
std::array<char, STRING_LENGTH+1> cdata;
public:
fixedString(const std::string &s)
: fixedString(s.c_str(), s.size())
{
}
fixedString(const char *s)
: fixedString(s, std::strlen(s))
{
}
fixedString(const char *s, const size_t len)
{
std::copy_n(s, std::min<size_t>(len, STRING_LENGTH), cdata.begin());
if (len < STRING_LENGTH)
std::fill_n(cdata.begin()+len, STRING_LENGTH-len, ' ');
cdata[STRING_LENGTH] = '\0';
}
friend std::ostream& operator<<(std::ostream& os, const fixedString &fs)
{
os << fs.cdata.data();
return os;
}
};
struct parameter {
double value;
fixedString name;
parameter(double _value, const fixedString &_name)
: value(_value), name(_name)
{
}
};
Online Demo
Related
I have an API:
void func(struct s st)
{
//do some stuff
}
when struct s define as:
struct s
{
char* str1;
char* str2;
};
Now, I want to call func() multiple times with different pairs of string literals.
So I wanted store my structs in iterable container. for example std::vector:
const std::vector <struct s> sts=
{
{
"str1",
"str2"
},
{
"str3",
"str4"
}
...
};
for(auto& st : sts)
{
func(st);
}
But I get an error:
ISO C++ forbids converting a string constant to ‘char*’.
I know the problem is that I try to assign string literal (const char*) to char*, but how can I fix it?
I know I can implement init function (or define), and call it every time. something like:
s init_struct(const char* str1, const char* str2)
{
char *str1_ = strdup(str1);
char *str2_ = strdup(str2);
return {str1_, str2_};
}
but I want my code simply as possible. also, I can't change func() prototype or struct s declaration.
My question is: what is the fastest and cleanest way to initialize iterable container with the structs?
When I say "fastest" I don't mean to performance (my code doesn't run in real time), but in terms of readability.
In C++, the type of a string literal is an array of const char. Hence, you should define your structure to take a const char *:
struct s
{
const char* str1;
const char* str2;
};
While it would work to use string, this creates unnecessary copies, since the string literals are not going away. (If you want to use a more C++ type, at least use string_view rather than string, but given how simple this case is, I think const char * is just fine.)
First thing to recognize, you'll need to copy somewhere. On some architectures, cstring literals are in a different kind of memory than char*, so to be standard compliant, you need this.
Second trick, a std::initializer_list<T> can be initialized from a class that descends from T. Thus, you can add a class (scons) that has the proper constructor (and descends from s). While in general you cannot convert container<scons> to container<s>, you can still initialize a vector<s> as it'll look for std::initializer_list<s>, which can be constructed this way. Easier to tell by code:
#include <vector>
#include <string.h>
struct s
{
char* str1;
char* str2;
};
// even this could be templated, as arg count can be deduced using structured binding in c++17
struct scons : s
{
scons(const char* s1, const char* s2)
: s{strdup(s1), strdup(s2)} {}
};
int main() {
std::vector<s> v =
{
scons{
"str1",
"str2"
},
{
"str3",
"str4"
},
{
"str5",
"str6"
}
};
}
Note that you only need to specify the new ctor for the first element, the rest is auto-deduced.
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
struct s
{
char* str1;
char* str2;
s(){
}
s(string s1, string s2){
str1=new char[s1.size()+1];
str1[s1.size()]=0;
for(int i(0);i<s1.size();++i)
str1[i]=s1[i];
str2=new char[s2.size()+1];
str2[s2.size()]=0;
for(int i(0);i<s2.size();++i)
str2[i]=s2[i];
}
};
int main(){
const std::vector <struct s> sts=
{
{
"str1",
"str2"
},
{
"str3",
"str4"
}
};
return 0;
}
Is that ok?
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 am trying to overload the subscript operator -i know it as the element access operator- to take a char * or a std::string.
I have a struct
struct foo
{
int Age;
const char *Name;
};
and a std::vector that will be holding multiple instances of this struct.
std::vector<foo>bar;
my goal is to be able to access each foo in bar by calling them by their name.
std::cout<<"Simon's age is: "<<bar["simon"];
I've just been searching google for a long while trying to find an example or something to go off, with no luck.
i thought that this would work
foo operator[](char *Name)
{
for(int i=0;i<bar.size();i++)
{
if(bar[i].Name == Name){return bar[i];}
}
}
however apparently i'm doing something wrong
I do know this could be done with an std::map but i'd rather use an std::vector, thanks.
I would greatly appreciate your help however you choose to offer it. Thank you .
To do what you want requires inheriting from std::vector. Otherwise you can't overload its methods.
Something like the following (untested).
struct fooVector : public std::vector<foo> {
typedef std::vector<foo> super;
using super::size;
...
foo& operator[](char const* Name) {
super& self=*this;
for(int i=0;i<size();i++)
{
if( ! strcmp( self[i].Name, Name) ){return self[i];}
}
// Need to do something reasonable if not found
}
};
bar[i].Name == Name
was probably meant to be
strcmp(bar[i].Name, Name) == 0
However, you'll be better off using std::strings than managing plain char pointers.
And instead of inheriting form a vector, create a class with a vector as a member and have your operator[] on that class.
First of all there are some problems with your test:
1) you're using const char* for string type, comparing with == would likely fail as you're comparing two pointers and not their content. you need to use strcmp or better use std::string (c++ way)
2) what if your index operator would not find the value you're looking for?
I don't think this is a proper way of doing this but in case you really want to use vector you can inherit your own class and define new operator which uses const char* as a index argument:
#include <vector>
#include <iostream>
#include <string.h>
struct foo {
int age;
const char *name;
};
class MyVector : public std::vector<foo> {
public:
const foo* operator[](const char* name) const {
for (auto it=cbegin(); it != cend(); ++it) {
if (strcmp(it->name, name) == 0) {
return &(*it);
}
}
return nullptr;
}
};
int main(int argc, char *argv[]) {
foo foo1 = { 10, "abc" };
foo foo2 = { 20, "test" };
MyVector v;
v.push_back(foo1);
v.push_back(foo2);
std::cout << "test: " << v["test"]->age << std::endl;
}
Although it's not generally advised to inherit from stl containers (they don't have virtual destructors), if you don't add any data attributes to inherited class you should be fine.
But I would suggest you to consider using std::map as a container and std::string as index / name attribute type. Searching vector has linear complexity but std::map has logaritmic complexity. Or you can consider using hash tables.
I am a real c++ beginner and I have a problem with my char array output in a c++ excerise. I was asked to transform a certain UML class in to c++ and generate an working output with the parameters given in main. Here ist the code:
#include <iostream>
#include <stdlib.h>
/*My class defintion book*/
class Book
{ protected:
long int number;
char author[25];
int year;
bool lent;
void setLent(bool x);
bool getLent();
public:
Book(long int n, char a[25], int j, bool x);
long int getNr();
int getYear();
void print();
};
/*Method definition Book*/
Book::Book(long int n, char a[25], int j, bool x)
{number=n;
author=a;
year=j;
lent=x;}
long int Book::getNr()
{return number; }
int Book::getYear()
{return year;}
void Book::setLent(bool x)
{lent=x;}
bool Book::getLent()
{return lent;}
void Book::print()
{
std::cout << "Book Nr: " << number << std::endl;
std::cout << "Author: " << author << std::endl;
std::cout << "Year: " << year << std::endl;
if (lent==0)
std::cout << "Lent [yes/no]: no" << std::endl;
else
std::cout << "Lent [yes/no]: yes" << std::endl;
}
/*MAIN*/
int main()
{
Book b1(123456, "test", 2014, false);
b1.print();
system("pause");
return 0;
This is my output:
Book Nr: 123456
Author: b<Vv-[[vóYA
Year: 2014
Lent [yes/no]: no
Press any key to continue...
As you can see all outputs work except for the "Author". There I am getting crap. Note that I have to use char as type. since it is given in the UML class I had to transform into c++.
I really searched everywhere. But didn't find the correct solution. I have the feeling it will be a very simple one...
Thanks in advance for your help!
The reason this doesn't work is that you're assigning your pointer author to another pointer a, which then goes out of scope... so you're left with author pointing to some garbage. If you want to stick with character arrays, you'll have to copy all the data that a points to:
strcpy(author, a);
But since it's C++, you should just use strings, which are easier to deal with:
class Book {
...
std::string author;
....
};
Book::Book(long int n, const std::string& a, int j, bool x)
: author(a), ...
{ }
You are printing out uninitialized data.
Make author a string
#include <string>
class Book
{ protected:
long int number;
std::string author;
int year;
bool lent;
and make the argument to the constructor a string as well
Book::Book(long int n, const std::string& a, int j, bool x)
Arrays of characters are not as flexible as std::strings. they are just chunks of data. If you want to use strings then use std::string instead.
Also, use an initializer list in C++ constructors, not java style
Book::Book(long int n, const std::string &a, int j, bool x)
: number(n),
author(a),
year(j),
lent(x)
{ }
There are two bugs in your code:
Book::Book(long int n, const char a[25], int j, bool x)
{
number=n;
strncpy(author, a, 25); // author = a; doesn't work! shouldn't compile either...
year=j;
lent=x;
}
First: The variable author is a pointer to a zero terminated string. You can use strcpy() to copy this string. Therefore you need to #include <memory.h. But you need to be sure that the string -is- really zero-terminated and fits into your target variable! Else you'll overwrite other memory regions next to the target variable, which is also called a buffer overflow! Better use strncpy(target, source, maxlength); which avoids this problem.
Second: Your parameter a should be "const" as you want to be able to call it with a string constant like in Book b1(123456, "test", 2014, false); where "test" is a constant!
As others already suggested you should use std::string instead of a[25]. C-Strings are "C" and not "C++" and you should try to avoid them. C-Strings can introduce a lot of bugs into your code and enable buffer overflows (=security problems), too. Also they are more complicated to handle. You need to #include <string> to use them.
I've been trying for the last three day to figure out how to implement a generic way of getting the value out of a boost::variant<...>, but it's been quite difficult.
Here is the solution I could come up with, which it not at all generic:
#include <iostream>
#include "boost\variant\variant.hpp"
using MyVariant = boost::variant<int, std::string>;
class VariantConverter : public boost::static_visitor<>
{
private:
mutable int _int;
mutable std::string _string;
static VariantConverter apply(MyVariant& v)
{
VariantConverter res;
v.apply_visitor(res);
return res; // copy will be elided, right?
}
public:
void operator()(int& i) const
{
_int = i;
}
void operator() (std::string& s) const
{
_string = s;
}
static int to_int(MyVariant v)
{
return apply(v).from_int();
}
static std::string to_string(MyVariant v)
{
return apply(v).from_string();
}
int from_int()
{
return _int;
};
std::string from_string()
{
return _string;
};
};
int main()
{
using namespace std;
MyVariant v = 23;
int i = VariantConverter::to_int(v);
cout << i << endl;
v = "Michael Jordan";
std::string s = VariantConverter::to_string(v);
cout << s.c_str() << endl;
cin.get();
return 0;
}
I'd appreciate it if someone could guide me towards a better solution.
Or perhaps someone could explain to me the rationale behind this:
if I declare a:
using MyVariant = boost::variant<int, std::string>;
and then a:
ConverterToInt : basic_visitor<int> {
public:
int operator() (int i) { return i; };
};
Why is it that when I try to apply the ConverterToInt to a MyVariant as such:
ConverterToInt cti;
MyVariant i = 10;
i.apply_visitor(cti);
I get a compiler error about trying to find a operator() that takes a std::string?
It seems to me that apply_visitor is trying to call an operator() for each of the types MyVariant can take. Is that so? If it is, why? How can i avoid this behavior?
Cheers!
You can avoid the error message by telling ConverterToInt what to do with a std::string. You might know that i can't be a std::string but it's unreasonable to expect the compiler to know that (and if it is true, why are you using a variant?).
apply_visitor will only call the correct operator() method, but it decides at run time, and the compiler needs to have all the possibilities covered to generate the code.
MyVariant iv = 10;
int i = boost::get<int>(iv);
boost::variant does not "call" each operator() of an interface when invoked, but it must be able to. That's the entire point. A variant can hold any of the template types, so if you want to define an operation on it, you must specify somewhere what that operation means for each type.