How to cram map array in buffer to send? - c++

I am trying to move the map array but for this I need to put everything in buffer, here is my code.
#include <iostream>
#include <map>
#include <string>
std::map<std::int, std::string> maphack;
maphack.emplace(1, "CS");
maphack.emplace(2, "PBG");
maphack.emplace(3, "Maicraft");
maphack.insert_or_assign(3, "GTA5");
for (auto &a : maphack) {
std::cout << a.first << " : " << a.second << '\n';
}
How to put everything above in the buffer?
char buffer[64];
send(sock, buffer, AmountToSend, 0);

The simple answer is to use std::ostringstream and std::istringstream which allows to use std streams machinery with a string storage. Which can be converted to char* with c_str() method if needed later, full example is below.
The better answer can be to use more heavy lifting library for serialization, the choice of the format will depend on your program. Do you need binary or text format? Do you want other programs (in other programming languages) be able to read it? Do you need to support different endian system? Do you need to handle error when deserializing malformed input? Boost, Qt, google protobuf, and other ones are out there.
#include <iostream>
#include <map>
#include <sstream>
#include <string>
void write_map(const std::map<int, std::string> &m, std::ostream &out) {
for (auto &a : m) {
out << a.first << " " << a.second << '\n';
}
}
std::map<int, std::string> read_map(std::istream &in) {
std::map<int, std::string> m;
int i;
std::string s;
while (in >> i >> s) {
m.emplace(i, s);
}
return m;
}
int main() {
std::map<int, std::string> m;
m.emplace(1, "CS");
m.emplace(2, "");
m.emplace(3, "Maicraft");
m.insert_or_assign(3, "GTA5");
std::ostringstream out;
write_map(m, out);
std::string data = out.str();
std::cout << "Data:\n" << data << std::endl;
// send data over socket
// ...
std::istringstream in(data);
auto m1 = read_map(in);
std::cout << "Read:\n";
write_map(m1, std::cout);
std::cout << std::endl;
}

Related

How can I concatenate the elements of a vector of integers to a string in C++?

I've been trying to put every single element of a vector of integers into a string. I want to achieve this by type casting the integers into strings, after that I cocatenate those "small strings" into a single big string, which is going to represent all the elements of that specific vector.
This may look silly, but is really useful if you want to make a function that returns a vector like-a-thing, or etc.
The only problem is that I'm getting an error on line 13, which says :
error: no matching function for call to ‘std::__cxx11::basic_string<char>::basic_string(int&)’
13 | myString += (string) myVector[i];
| ^
and I don't have the slightest idea on why this is happening. My code follows below :
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int myVector[5] = {1,2,3,4,5};
string myString = "";
for (int i =0; i < 5; i++)
{
myString += (string) myVector[i];
myString += "\n";
}
cout << myString << endl;
any help will be much appreciated.
You can use std::to_string to convert an int to a std::string.
Change this line:
myString += (string) myVector[i];
To:
myString += std::to_string(myVector[i]);
Note: concatenating strings like that might not be so efficient due to temporary strings being created and destroyed (although it is likely that small strings optimization will kick in, so no additional heap allocations will take place).
As #Someprogrammerdude commented, you can consider to use std::ostringstream.
Side notes:
You are missing #include <string>.
Why is "using namespace std;" considered bad practice?.
You can use the fmt library:
fmt::join will accept a range, in your case a vector of ints, and join its contents with a given separator (e.g. an empty string if you just want all of the elements together).
fmt::format will create a string with a given format, in this case just the contents of the joined vector.
Demo
#include <fmt/ranges.h>
int main() {
int myVector[5] = {1,2,3,4,5};
auto myString = fmt::format("{}", fmt::join(myVector, ""));
fmt::print("{}\n", myString);
}
// Outputs: 12345
Or, simpler, if you don't need the string:
int main() {
int myVector[5] = {1,2,3,4,5};
fmt::print("{}\n", fmt::join(myVector, ""));
}
The error you are getting is saying that the compiler cannot find a std::__cxx11::basic_string<char>::basic_string(int&) function, i.e., a std::string constructor accepting an int&.
You can use std::stringstream
#include <iostream>
#include <string>
#include <sstream>
int main()
{
int myVector[5] = {1,2,3,4,5};
std::string myString;
std::stringstream sstream;
for (auto i : myVector)
sstream << i;
sstream >> myString;
std::cout << myString;
}
Link.
I'll add my own solution, as laid out in my comment:
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
int main()
{
std::vector myvector = { 1, 2, 3, 4, 5 };
std::copy(std::begin(myvector), std::end(myvector),
std::ostream_iterator<int>(std::cout, "\n"));
}
Overload the output stream operator, and then you have something reusable for a lot of scenarios.
Based on the feedback below overloading is not the best answer, another approach here : https://www.onlinegdb.com/zDUjVbSTp
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
// Overloading operator<< will have many benefits.
// you can use it to output an array to std::cout directly
// or you can write it to a file or stringstream
std::ostream& operator<<(std::ostream& os, const std::vector<int>& values)
{
os << "[";
bool comma = false;
for (const auto& value : values)
{
if (comma) os << ", ";
os << value;
comma = true;
}
os << "]";
return os;
}
int main()
{
std::vector<int> values{ 1,2,3,4,5 };
// write directly to std::cout
std::cout << "direct : " << values << "\n";
// adding array to a string
std::ostringstream os;
std::string string{ "output = " };
os << values;
string += os.str();
std::cout << string << "\n";
return 0;
}
You can use for_each algorithm as well to do the concatenation.
#include <iostream>
#include <algorithm>
int main()
{
std::vector<int> v{1, 2, 3, 4, 5, 6};
std::string all;
std::for_each(v.begin(), v.end(), [&, del=""](const int &e) {
all += std::to_string(e) + (&e == &v.back() ? "" : del);
});
std::cout << all << std::endl;
}
output:
123456
If you want to add a delimiter in between, just change the del value in lambda capture.
std::for_each(v.begin(), v.end(), [&, del="-"](const int &e) {
all += std::to_string(e) + (&e == &v.back() ? "" : del);
});
Output:
1-2-3-4-5-6

How to parse two strings using boost::spirit?

I am still trying to wrap my head around Boost::Spirit.
I want to parse two words into a variable. When I can do that, into a struct.
The single word compiles, the Variable doesn't. Why?
#include <boost/spirit/include/qi.hpp>
#include <boost/tuple/tuple.hpp>
#include <string>
#include <iostream>
using namespace boost::spirit;
/*
class Syntax : public qi::parser{
};
*/
int main()
{
//get user input
std::string input;
std::getline(std::cin, input);
auto it = input.begin();
bool result;
//define grammar for a single word
auto word_grammar = +qi::alnum - qi::space;
std::string singleWord;
result = qi::parse(
it, input.end(),
word_grammar,
singleWord
);
if(!result){
std::cout << "Failed to parse a word" << '\n';
return -1;
}
std::cout << "\"" << singleWord << "\"" << '\n';
//Now parse two words into a variable
std::cout << "Variable:\n";
typedef boost::tuple<std::string, std::string> Variable;
Variable variable;
auto variable_grammar = word_grammar >> word_grammar;
result = qi::parse(
it, input.end(),
variable_grammar,
variable
);
if(!result){
std::cout << "Failed to parse a variable" << '\n';
return -1;
}
std::cout << "\"" << variable.get<0>() << "\" \"" << variable.get<1>() << "\"" << '\n';
//now parse a list of variables
std::cout << "List of Variables:\n";
std::list<Variable> variables;
result = qi::parse(
it, input.end(),
variable_grammar % +qi::space,
variable
);
if(!result){
std::cout << "Failed to parse a list of variables" << '\n';
return -1;
}
for(auto var : variables)
std::cout << "DataType: " << var.get<0>() << ", VariableName: " << var.get<1>() << '\n';
}
In the end I want to parse something like this:
int a
float b
string name
Templates are nice, but when problems occur the error messages are just not human readable (thus no point in posting them here).
I am using the gcc
Sorry to take so long. I've been building a new web server in a hurry and had much to learn.
Here is what it looks like in X3. I think it is easier to deal with than qi. And then, I've used it a lot more. But then qi is much more mature, richer. That said, x3 is meant to be adaptable, hackable. So you can make it do just about anything you want.
So, live on coliru
#include <string>
#include <iostream>
#include <vector>
#include <boost/spirit/home/x3.hpp>
#include <boost/tuple/tuple.hpp>
//as pointed out, for the error 'The parser expects tuple-like attribute type'
#include <boost/fusion/adapted/boost_tuple.hpp>
//our declarations
using Variable = boost::tuple<std::string, std::string>;
using Vector = std::vector<Variable>;
namespace parsers {
using namespace boost::spirit::x3;
auto const word = lexeme[+char_("a-zA-Z")];
//note, using 'space' as the stock skipper
auto const tuple = word >> word;
}
std::ostream& operator << (std::ostream& os, /*const*/ Variable& obj) {
return os << obj.get<0>() << ' ' << obj.get<1>();
}
std::ostream& operator << (std::ostream& os, /*const*/ Vector& obj) {
for (auto& item : obj)
os << item << " : ";
return os;
}
template<typename P, typename A>
bool test_parse(std::string in, P parser, A& attr) {
auto begin(in.begin());
bool r = phrase_parse(begin, in.end(), parser, boost::spirit::x3::space, attr);
std::cout << "result:\n " << attr << std::endl;
return r;
}
int main()
{
//not recomended but this is testing stuff
using namespace boost::spirit::x3;
using namespace parsers;
std::string input("first second third forth");
//parse one word
std::string singleWord;
test_parse(input, word, singleWord);
//parse two words into a variable
Variable variable;
test_parse(input, tuple, variable);
//parse two sets of two words
Vector vector;
test_parse(input, *tuple, vector);
}
You may like this form of testing. You can concentrate on testing parsers without a lot of extra code. It makes it easier down the road to keep your basic parsers in their own namespace. Oh yea, x3 compiles much faster than qi!
The single word compiles, the Variable doesn't. Why?
There are missing two #includes:
#include <boost/fusion/adapted/boost_tuple.hpp>
#include <boost/spirit/include/qi_list.hpp>

How to insert vector of integers into Key, Value of std::map

Goal: Read numerical text files into vectors and then add the vectors to key,value std::map so that I can reference them by the key name I have specified for them, later.
Thought this would be easy and I am surprised that I can't find an answer for this already on StackOverflow.
Result Expected:
Print1 = {100,200,500,600}
Print2 = {7890,5678,34567,3,56}
Print3["NameA"] = Print1
Print3["NameB"] = Print2
If my process is inefficient or going in the wrong direction, I would appreciate the pointers.
I keep getting Semantic Issue build fails and no viable conversion from pair <const basic_string>
Current Code:
#include <string.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
int main()
{
std::vector<int> print1;
std::ifstream inputFile("numbers.txt");
// test file open
if (inputFile)
{
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
print1.push_back(value);
}
}
inputFile.close();
std::vector<int> print2;
std::ifstream inputFile2("numbers2.txt");
// test file open
if (inputFile2)
{
double value;
// read the elements in the file into a vector
while ( inputFile2 >> value ) {
print2.push_back(value);
}
}
inputFile2.close();
std::map<std::string, std::vector<int>> contacts;
contacts["alice"] = print1;
contacts["bob"] = print2;
std::vector<std::string> keys(contacts.size());
std::vector<int> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
std::cout << "Keys:\n";
copy(keys.begin(), keys.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Values:\n";
copy(values.begin(), values.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
You can reference map element directly which will create an entry if doesn't exist, and just fill it from the file read loop:
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> m;
int num;
auto &&alice = m["alice"];
std::ifstream if_alice("numbers1.txt");
while (if_alice >> num)
alice.push_back(num);
if_alice.close();
auto &&bob = m["bob"];
std::ifstream if_bob("numbers2.txt");
while (if_bob >> num)
bob.push_back(num);
if_bob.close();
// test
for (auto &&data : m)
{
std::cout << "Name: " << data.first << "\t";
for (int num : data.second)
std::cout << num << " ";
std::cout << "\n";
}
}
First of all, there is no point in arguing that Xcode didn't show any error msgs for your code. Try either turning on all the compiler warnings or try in online compilers. The result will be not disappointing: https://godbolt.org/z/cU54GX
If I have understood correctly, you want to store your information from two files(the integer values) in a std::map, where its key = std::string and value = vector of integer array.
If so,
1. You have your problem starting from reading integers from files.
There you are using double for no reason and storing to
std::vector<int> (i,e print1 and print2).
2. Secondly, what if your file has not been open?. inputFile.close();
and inputFile2.close(); will close it anyway, without knowing the
fact. This is wrong.
The proper way would be:
inputFile.open("numbers.txt", std::ios::in); // opening mode
if (inputFile.is_open()) {
// do stuff
inputFile.close(); // you need closing only when file has been opened
}
3. If your intention is to only print keys and values, you don't
need to parse them to different vectors.
You can do it directly:
for(const std::pair<kType, vType>& mapEntry: contacts)
{
std::cout << "Key: " << mapEntry.first << " Values: ";
for(const int values: mapEntry.second) std::cout << values << " ";
std::cout << std::endl;
}
In c++17 you can use Structured binding
for(const auto& [Key, Values]: contacts)
{
std::cout << "Key: " << Key << " Values: ";
for(const int value: Values) std::cout << value << " ";
std::cout << std::endl;
}
4. If you really want to parse them to a different vector; first of all, the data structure for storing keys is wrong:
std::vector<int> values(contacts.size());
^^^^^^
which should have been a vector of vector of integers, as your vType = std::vector<int>. That is,
std::vector<std::vector<int>> values
^^^^^^^^^^^^^^^^^^
Secondly, you have the functions key(const std::pair<std::string, std::string>& keyValue) and value(const std::pair<std::string, std::string>& keyValue) wrong, where you are passing a pair of strings as keys and values.
This should have been
typedef std::string kType; // type of your map's key
typedef std::vector<int> vType;// value of your map's value
std::pair<kType, vType>
However, you can simply replace with lambdas, which would be more intuitive, in the sense of having the functions next to the line where you needed. For example,
std::vector<kType> keysVec;
keysVec.reserve(contacts.size());
auto getOnlyKeys = [](const std::pair<kType, vType>& mapEntry){ return mapEntry.first; };
std::transform(contacts.begin(), contacts.end(), std::back_inserter(keysVec), getOnlyKeys);
See an example code here

C++ cereal de-serialization trouble with large size vector

I hope to serialize large size vector with cereal, C++ serialization library.
But, if trying to do that, the exception "Failed to read " + std::to_string(size) + " bytes from input stream! Read " + std::to_string(readSize)" is thrown.
Does anyone know a good solution for this?
I'm using VisualStudio 2017.
The source code is shown below.
#include <iostream>
#include <fstream>
#include "include\cereal\cereal.hpp"
#include "include\cereal\archives\binary.hpp"
#include "include\cereal\types\vector.hpp"
#include "include\cereal\types\string.hpp"
void show(std::vector<int> v) {
for (auto i : v)std::cout << i << ",";
std::cout << std::endl;
}
int main(void) {
const std::string file_name = "out.cereal";
{
std::vector<int> src;
// const int STOP = 10; //OK
const int STOP = 1000; // NG
for (int i = 0; i < STOP; i++)src.push_back(i);
std::cout << "src:" << std::endl;
show(src);
std::ofstream ofs(file_name, std::ios::binary);
cereal::BinaryOutputArchive archive(ofs);
archive(src);
}
{
std::vector<int> dst;
std::fstream fs(file_name);
cereal::BinaryInputArchive iarchive(fs);
iarchive(dst);
std::cout << "dst:" << std::endl;
show(dst);
}
#ifdef _MSC_VER
system("pause");
#endif
return 0;
}
You code works fine for me in Linux, so I think it is to do with the difference between text and binary handling on Windows. Check that you pass std::ios::binary when you are constructing the input stream. Also construct it as std::ifstream rather than just std::fstream.
I think this might have to do with Windows expecting (or adding) a Unicode byte-order mark, which is confusing the serializer.

Convert a vector<char> to a string with a conversion

I'd like to convert a vector<char> to a std::string and do a conversion one the way.
I'm almost there, but the result of the code below is a vector<string>, while I'd like to have one string (a concatenation of all the string parts in the vector).
See my code example for details.
string ConvertHexToAscii(const char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return oss.str();
}
vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
vector<string> resultVec;
std::transform(readBuffer.begin(), readBuffer.end()
, back_inserter(resultVec)
, ConvertHexToAscii);
// resultVec[0] = "1C";
// resultVec[1] = "09";
The result I need is a string containing "1C09". How to achieve that with std::transform?
You were almost there; this works:
std::stringstream sstr;
std::transform(
input.begin(), input.end(),
std::ostream_iterator<std::string>(sstr, ""),
ConvertHexToAscii);
But unfortunately this instantiates quite a lot of string streams, which is inefficient. Ideally, the ConvertHexToAscii (misnamed, by the way! C++ doesn’t know about encodings) function would directly use the underlying stream.
#include <iostream>
#include <vector>
#include <iomanip>
#include <sstream>
#include <numeric>
std::string ConvertHexToAscii(std::string acc, char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return acc + oss.str();
}
int main() {
std::vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
std::cout << std::accumulate(readBuffer.begin(), readBuffer.end()
, std::string(), ConvertHexToAscii) << std::endl;
return 0;
}
create your own back_insert_iterator (look at the code in your stl lib, it's fairly simple) for string types of which operator = is defined as
template< class string_type, class value_type >
class back_insert_iterator
{
public:
back_insert_iterator< _string_type >& operator = ( const value_type& val )
{
container->append( val )
return *this;
}
};
You can do this with a function output iterator:
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <boost/function_output_iterator.hpp>
std::string ConvertHexToAscii(const char input)
{
std::ostringstream oss;
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(input);
return oss.str();
}
int main() {
std::vector<char> readBuffer; // this is my input
readBuffer.push_back(0x1c);
readBuffer.push_back(0x09);
std::string temp;
std::transform(readBuffer.begin(), readBuffer.end()
, boost::make_function_output_iterator([&temp](const std::string& r) {temp.append(r);})
, ConvertHexToAscii);
std::cout << temp << std::endl;
}
I used a lambda to call the append() function on the result string, but if you don't have that available it's fairly easy to use boost::bind or just write an old fashioned functor to do that for you.
With boost::bind the function output iterator gets created as:
boost::make_function_output_iterator(boost::bind(static_cast<std::string& (std::string::*)(const std::string&)>(&std::string::append), &temp, _1))
instead. It's slightly clunky because you need to pick the right overload for std::string::append.
Whereas perreal's idea of using an accumulation isn't that bad, it may be more performant to operate on the stream directly instead of creating so many temporary strings (though move semantics may help with that):
std::ostringstream os;
std::string temp = std::accumulate(
readBuffer.begin(), readBuffer.end(), std::ref(os),
[](std::ostream &os, const char input)
-> std::reference_wrapper<std::ostream> {
return os << std::hex << std::setw(2)
<< std::setfill('0')
<< static_cast<int>(input);
}).str();
EDIT: But Ok, that's maybe a bit oversophistication here, a simple foreach would have done, too (even if not as semantically clean as an accumulation):
std::ostringstream os;
std::for_each(readBuffer.begin(), readBuffer.end(),
[&os](const char input) mutable {
os << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(input);
});
std::string temp = os.str();
But anything might be better than creating a whole bunch of temporary strings.