Why basic_ifstream show me wrong results? - c++

I have a binary file. There are 2288*2288 longitude float values stored in top half section, and the same number of latitude float values occupied the bottom half. I used the following code to load them into a float vector. It can run like a charm, but gave me incorrect results. With regard to my binary file, the float vector should be filled with a total of 2288*2288*2=10469888 elements, but only 159005, all their values are the same 200.0000. Would you please explain what's wrong with my code?
Thank you in advance!
bool LoadData(const char* pszDataFile)
{
typedef char_traits<float> traits_type;
typedef std::codecvt<float, char, mbstate_t> cvt;
std::basic_ifstream<float, traits_type> input( pszDataFile, std::ios::binary );
std::locale loc(std::locale(), new cvt());
input.imbue(loc);
std::vector<float> fvBuffer;
// Copies all data into buffer
std::copy(std::istreambuf_iterator<float>(input),
std::istreambuf_iterator<float>( ),
std::back_inserter(fvBuffer));
long nSzie = fvBuffer.size(); // Wrong vector size (159005)
return true;
}

If you imbue() a file stream after the file is opened the imbue() will silently fail.
You must do the imbue() then open the file:
std::basic_ifstream<float, traits_type> input;
std::locale loc(std::locale(), new cvt());
input.imbue(loc);
// Open after the imbue()
input.open( pszDataFile, std::ios::binary );
Your second problem is you are confusing the terms binary file:
This code reads a stream of text data (i.e. a text file) using the operator >>
std::copy(std::istreambuf_iterator<float>(input),
std::istreambuf_iterator<float>( ),
std::back_inserter(fvBuffer));
The use of std::ios::binary when you open the file only affects the how the 'end of line sequence' (EOLS) is generated it has nothing to do with the nature of the file. Though because you specify it the '\n' character is not converted into the EOLS which is useful when you are generating binary files.
So the short answer is you better by reading a text stream from a file.
The reason your vector is shorter than what you expect is because the stream reading encountered an error and as a result stopped responding to read requests.
Additionally I can't get your code to compile.
So you are doing something else non-standard.

To make std::basic_ifstream works, you have to define your trait_type so that it provides everything expected by input stream, and I'm not sure that it will be possible. That will be far more than just a codecvt<float, char, mbstate_t> (which you seem to think is already present while the standard demand only the wchar_t, char and char, char specializations).
If you want a binary input iterator, you'll have to write one yourself to work with basic_ifstream, something like this (run and give the expected result, but not debugged further):
#include <fstream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <iostream>
template <typename T>
class BinaryInputIterator
: public std::iterator<std::input_iterator_tag, T, std::ptrdiff_t,
const T*, const T&>
{
public:
BinaryInputIterator();
BinaryInputIterator(std::istream&);
// Compiler generated version OK:
// BinaryInputIterator(BinaryInputIterator const& other);
// BinaryInputIterator& operator=(BinaryInputIterator const& other);
~BinaryInputIterator();
T const& operator*() const;
T const* operator->() const;
BinaryInputIterator& operator++();
BinaryInputIterator operator++(int);
private:
std::istream* myStream;
T myValue;
friend bool operator==
(BinaryInputIterator const& l, BinaryInputIterator const& r)
{
return ((l.myStream == NULL && (r.myStream == NULL || !*r.myStream))
|| (r.myStream == NULL && (l.myStream == NULL || !*l.myStream)));
}
friend bool operator!=
(BinaryInputIterator const& l, BinaryInputIterator const& r)
{
return !(l == r);
}
};
template <typename T>
BinaryInputIterator<T>::BinaryInputIterator()
: myStream(0)
{}
template <typename T>
BinaryInputIterator<T>::BinaryInputIterator(std::istream& is)
: myStream(&is)
{
myStream->read(reinterpret_cast<char*>(&myValue), sizeof myValue);
}
template <typename T>
BinaryInputIterator<T>::~BinaryInputIterator()
{}
template <typename T>
T const& BinaryInputIterator<T>::operator*() const
{
return myValue;
}
template <typename T>
T const* BinaryInputIterator<T>::operator->() const
{
return &myValue;
}
template <typename T>
BinaryInputIterator<T>& BinaryInputIterator<T>::operator++()
{
myStream->read(reinterpret_cast<char*>(&myValue), sizeof myValue);
return *this;
}
template <typename T>
BinaryInputIterator<T> BinaryInputIterator<T>::operator++(int)
{
BinaryInputIterator result(this);
++*this;
return result;
}
int main()
{
{
std::ofstream os("foo.dta");
std::vector<float> vect1;
vect1.push_back(4.2);
vect1.push_back(3.14);
os.write(reinterpret_cast<char*>(&vect1[0]), sizeof(float)*vect1.size());
}
{
std::ifstream is("foo.dta");
std::vector<float> vect2;
std::copy(BinaryInputIterator<float>(is),
BinaryInputIterator<float>(),
std::back_inserter(vect2));
std::copy(vect2.begin(), vect2.end(), std::ostream_iterator<float>(std::cout, "\n"));
}
}

Related

How to make std::istream_iterator read only until the end of line?

Having the following piece of code:
std::vector<int64> values;
std::copy(
std::istream_iterator<int64>(std::cin),
std::istream_iterator<int64>(),
std::back_inserter(values)
);
I'd like to make it read the input stream only until the end of line. How can I do that with std::istream_iterator?
You can't do it with std::istream_iterator.
But it is relatively easy to write an input iterator.
#include <iterator>
#include <iostream>
#include <sstream>
#include <vector>
#include <cctype>
template<typename T>
class istream_line_iterator: public std::iterator<std::input_iterator_tag, T>
{
std::istream* stream;
public:
// Creating from a stream or the end iterator.
istream_line_iterator(std::istream& s): stream(&s) {dropLeadingSpace();}
istream_line_iterator(): stream(nullptr) {}
// Copy
istream_line_iterator(istream_line_iterator const& copy): stream(copy.stream) {}
istream_line_iterator& operator=(istream_line_iterator const& copy) {stream = copy.stream;return *this;}
// The only valid comparison is against the end() iterator.
// All other iterator comparisons return false.
bool operator==(istream_line_iterator const& rhs) const {return stream == nullptr && rhs.stream == nullptr;}
bool operator!=(istream_line_iterator const& rhs) const {return !(*this == rhs);}
// Geting the value modifies the stream and returns the value.
// Note: Reading from the end() iterator is undefined behavior.
T operator*() const {T value;(*stream) >> value;return value;}
T* operator->() const; // Not sure I want to implement this.
// Input streams are funny.
// Does not matter if you do a pre or post increment. The underlying stream has changed.
// So the effect is the same.
istream_line_iterator& operator++() {dropLeadingSpace();return *this;}
istream_line_iterator& operator++(int) {dropLeadingSpace();return *this;}
private:
void dropLeadingSpace()
{
// Only called from constructor and ++ operator.
// Note calling this on end iterator is undefined behavior.
char c;
while((*stream) >> std::noskipws >> c) {
if (c == '\n') {
// End of line. So mark the iterator as reaching end.
stream = nullptr;
return;
}
if (!std::isspace(c)) {
// Found a non space character so put it back
stream->putback(c);
return;
}
}
// End of stream. Mark the iterator as reaching the end.
stream = nullptr;
}
};
int main()
{
std::stringstream s{"0 1 2 3 4 5 6 7 8 9 10\n11 12 13 14 15 16\n17 18 19"};
std::vector<int> d{istream_line_iterator<int>(s), istream_line_iterator<int>()};
for(auto v: d) {
std::cout << "V: " << v << "\n";
}
}
Running:
> g++ -std=c++17 main.cpp
> ./a.out
V: 0
V: 1
V: 2
V: 3
V: 4
V: 5
V: 6
V: 7
V: 8
V: 9
V: 10
If you want the functionality without adding a std::getline or std::stringstream, you can change the character classification of the stream so that a newline is not considered discardable whitespace. Here is a minimal example:
struct set_newline_as_ws : std::ctype<char> {
static const mask* make_table( std::ctype_base::mask m ) {
static std::vector<mask> v(classic_table(), classic_table() + table_size);
v['\n'] &= m;
return &v[0];
}
set_newline_as_ws( bool skip, std::size_t refs = 0 ) : ctype(make_table(skip ? ~space : space), false, refs) {}
};
std::istream& skipnewline( std::istream& is ) {
is.imbue(std::locale(is.getloc(), new std::ctype<char>));
return is;
}
std::istream& noskipnewline( std::istream& is ) {
is.imbue(std::locale(is.getloc(), new set_newline_as_ws(true)));
return is;
}
int main() {
std::vector<int64> values;
std::cin >> noskipnewline;
std::copy(
std::istream_iterator<int64>(std::cin),
std::istream_iterator<int64>(),
std::back_inserter(values)
);
std::cin >> skipnewline;
}
Adapted from an earlier answer to a similar question (here):
#include <vector>
#include <algorithm>
#include <string>
#include <iterator>
namespace detail
{
class Line : public std::string
{
friend std::istream & operator>>(std::istream & is, Line & line)
{
return std::getline(is, line);
}
};
}
template<class OutIt>
void read_lines(std::istream& is, OutIt dest)
{
typedef std::istream_iterator<detail::Line> InIt;
std::copy_n(InIt(is), 1, dest);
}
int main()
{
std::vector<std::string> v;
read_lines(std::cin, std::back_inserter(v));
return 0;
}
This code should take only one line and saves it into the vector v. This is thanks to the use of std::copy_n function which takes the number of elements-to-be-copied as input. There is a quirk, though, reported here. Depending on your platform, one or two lines will be read even though only the first one will be saved into v.
That being said, if you want to make it fail-safe, you can just implement your own copy_n(...) function, like so:
template<class InIt, class Range, class OutIt>
OutIt own_copy_n(InIt first, Range count, OutIt dest)
{
*dest = *first;
while (0 < --count)
*++dest = *++first;
return (++dest);
}
Then instead of using std::copy_n(...) in you code, you can use own_copy_n(...).
This way you'll be sure you'll have to input one line only, and that that line will be saved into your vector v.
It is impossible to change istream_iterator in this way, because it has no knowledge of newlines, or even of characters, for that matter. It only knows about int64(Or whatever type you instantiated it with) and if the stream ended or failed (When it then will return false on operator bool()).
This means that our point of customization must be the actual stream.
The stream object is actually just a wrapper around a std::basic_streambuf. The streambuf will spit out characters until it finds an EOF. You can simply adapt it to return an EOF once it finds a newline, then the stream will temporarily treat that as the end of stream.
Doing this is easy: The streambuf object is accessible through the member funtion rdbuf(). You can just replace the buffer with your custom one using this function. To have it continue reading after the newline when you're done, you can just return the original std::basic_streambuf back to the stream.
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <streambuf>
#include <vector>
// This is our custom std::basic_streambuf object.
// We chose the underflow function as our point of customization, because
// it is there that the action is happening: it is this function that
// that is responsible for reading more data from the stream.
class newline_buf : public std::streambuf {
std::streambuf* src;
char ch; // single-byte buffer
protected:
int underflow() {
if( (ch= src->sbumpc()) == '\n') {
return traits_type::eof(); // return EOF on new line.
}
setg(&ch, &ch, &ch+1); // make one read position available
return ch; // may also return EOF by the regular means
}
public:
newline_buf(std::streambuf* buf) : src(buf) {
setg(&ch, &ch+1, &ch+1); // buffer is initially full
}
};
int main() {
// testing with a stringstream to make things easier to reproduce.
// Should work fine with any type of input stream.
std::istringstream iss(R"(12345 12345 12345
67890 67890 67890)");
// We store the original rdbuf so we can recover it later.
auto original_rdbuf = iss.rdbuf();
newline_buf buf(original_rdbuf);
iss.basic_ios::rdbuf(&buf);
// Do the copy and then recover the original rdbuf
std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(), std::ostream_iterator<int>(std::cout, " "));
iss.basic_ios::rdbuf(original_rdbuf);
// You can try doing a new copy, just to convince yourself that the stream is still in a valid state.
//std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(), std::ostream_iterator<int>(std::cout, " "));
}
See it live!

Writing/Reading a std::map to a binary file require operator

I want to write a std::map to a file and read it back. I'm looking for a rather simple and minimalist way to do it, without boost. I found that it is doable with vector like here Reading and writing a std::vector into a file correctly with iterators
I found this question as it relates to what I want to do, except I'm looking for the binary alternative.
reading a file of key-value pairs in to a std::map
For types with no dynamic memory (actually, pointers) involved
template<size_t N>
struct Serial
{
char bin[N];
friend ostream& operator<<(ostream& os, const Serial& s)
{
for(auto c : bin)
os << c;
return os;
}
friend istream& operator>>(istream& is, Serial& s)
{
for(auto& c : bin)
is >> c;
return is;
}
};
struct Key
{
static constexpr size_t size = sizeof(Key);
Key(const Serial<size>& s) { memcpy(this, s.bin, size); }
Serial<size>& serialize() { return *reinterpret_cast<Serial<size>*>(this); }
};
struct Value
{
static constexpr size_t size = sizeof(Value);
Key(const Serial<size>& s) { memcpy(this, s.bin, size); }
Serial<size>& serialize() { return *reinterpret_cast<Serial<size>*>(this); }
};
void write(ostream& os, const std::map<Key, Value>& m)
{
for(const auto& p : m)
os << p.first.serialize() << p.second.serialize();
}
void read(istream& is, std::map<Key, Value>& m)
{
Serial<Key::size> k;
Serial<Value::size> v;
while(is >> k >> v)
m[k] = v;
}
For types with dynamic memory (pointers) involved, the solution will be then entirely dependent on how they work, no magical solution can be provided.
Have you considered JSON?
Welcome to the messy, confusing, inconsistent world of serialization. Hope you enjoy the ride!!
This is an age-old problem: how to write a modestly complex data structure to some text or binary format, and then be able to later read it back. There are a couple of different ways to do this. However, you said you wanted to serialize to a binary format, so I would recommend using MessagePack.
There's a C++11 library for working with the MessagePack format called msgpack11 that's also rather lightweight, which would seem to fit your requirements. Here's an example:
std::map<A, B> my_map;
// To save my_map:
msgpack11::MsgPack msgpack{my_map};
std::string binary_data = msgpack.dump();
// Now you can save binary_data to a file.
// To get the map back:
string error_string;
auto msgpack = msgpack11::MsgPack::parse(binary_data, error_string);
std::map<A, B> my_map;
// Now you need to manually read back the data.
For binary writing you should use write method of ostream
ostream& ostream::write (const char* s, streamsize n);
See documentation here: http://en.cppreference.com/w/cpp/io/basic_ostream
You can't write map to file driectly, you should write it's represntation , developed by you. You would need to write each key/value pair individually or buffer them is a data block and write it into file.This really isn't much more complicated than a for loop, though. If map contains classes that aren't trivially constructed and destroyed, you should implement a method that allows to serialize class' binary data.
Binary implementations will necessarily be non-portable (for the resultant file). If that is not a concern then consider defining a custom allocator that uses a memory mapped file. You would then declare your std:map using that allocator as one of the template arguments. You could use that map directly, or using range insertion to save an existing map to a file. If the key or value require allocators (e.g. strings) you would have to do declare versions of those types using the memory mapped allocator in the template declaration and define assignment operators from the key/value type to the new types.
You can find some allocator implementations and further discussion by searching for "memory mapped file stl allocator". Also see: Memory mapped file storage in stl vector
void BinSerialize(ostream &out, int32_t x);
void BinSerialize(ostream &out, int16_t x);
void BinSerialize(ostream &out, int8_t x);
void BinSerialize(ostream &out, const string &s)
{
BinSerialize(out, (int32_t)s.size());
out.write(size.c_str(), s.size()));
}
temmplate<class KeyT, ValueT>
void BinSerialize(ostream &out, const std::map<KeyT, ValueT> &m)
{
BinSerialize(out, (int32_t)m.size());
for (auto& item : m)
{
BinSerialize(out, item.first);
BinSerialize(out, item.second);
}
}
void BinDeserialize(istream &input, int32& x);
void BinDeserialize(istream &input, int16& x);
void BinDeserialize(istream &input, int8& x);
void BinDeserialize(istream &input, string &s)
{
int32_t size;
BinDerialize(out, size);
s.resize(size);
out.read(size.c_str(), size);
}
temmplate<class KeyT, class ValueT>
void BinDeserialize(istream &input, std::map<KeyT, ValueT> &m)
{
int32_t size;
m.clear();
BinDeserialize(out, size);
for (int32_t i=0; i<size; ++i)
{
std::pair<KeyT, ValueT> item;
BinDeserialize(out, item.first);
BinDeserialize(out, item.second);
m.insert(item);
}
}
This is quickly written. It is possible to improve it with templates, to cover all basic types and all STL containers.
Also it would be nice to keep in mind about the endian.
It is better avoid use of overloaded operators in this case. But if you if to do it it is best to define class which will wrapp STL stream and will have own set of overloaded >> << operators. Take a look on Qt QDataStream.

Parse hex numbers along with decimal numbers from XML with boost::property_tree

I am parsing an XML file with boost::property_tree. The data I need to parse includes regular decimal numbers such as 42 and hex numbers such as 0xF1. For example:
<hex>0xF1</hex>
<dec>42</dec>
It's easy to parse decimal numbers and convert them to int with ptree::get<int>(). However, the same call on the hex number fails.
I can work around this by parsing the hex number as a std::string and then using std::istringstream and std::hex to convert it to int. Demonstrating with code:
#include <iostream>
#include <string>
#include <sstream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
using std::string;
namespace pt = boost::property_tree;
int main() {
pt::ptree tree;
try {
pt::read_xml("debug.xml", tree, pt::xml_parser::no_comments);
} catch (const pt::xml_parser_error&) {}
int hexnum;
// Doesn't work (throws exception)
try {
hexnum = tree.get<int>("hex");
} catch (const pt::ptree_bad_data&) {
std::cout << "caught bad ptree data exception";
}
// Workaround: parse as a string, then convert the string
string hexstring;
try {
hexstring = tree.get<string>("hex");
std::istringstream iss(hexstring);
iss >> std::hex >> hexnum;
if (!iss) throw std::ios_base::failure("invalid hex string");
} catch (const pt::ptree_error&) {
// get() failed
} catch (const std::ios_base::failure& fail) {
std::cout << fail.what();
}
// Parsing a regular decimal number is straightforward
int decnum;
try {
decnum = tree.get<int>("dec");
} catch (const pt::ptree_error&) {}
return 0;
}
Is there a more elegant way to do this, similar to how I can convert a std::string into a number using std::istringstream with std::hex, std::oct, or std::dec? The documentation shows that there is a stream_translator.hpp header file which looks promising as a way to do this -- but there's not much documentation for this file either on the Boost website or in the header file itself. The workaround with std::istringstream is acceptable, but stream_translator.hpp makes me wonder if boost::property_tree provides a way to do this.
I need to be able to easily switch between parsing hex and decimal numbers, just as it's easy to do so with iss >> std::hex or iss >> std::dec on std::istringstream iss.
(In case it matters, my compiler is VS2005. Yes, '05 not '15.)
The code of stream_translator looks pretty simple: it has only two methods. I guess you can write your own translator and setup a hex flag. Something like this:
/// Implementation of Translator that uses the stream overloads.
template <typename Ch, typename Traits, typename Alloc, typename E>
class stream_translator
{
typedef customize_stream<Ch, Traits, E> customized;
public:
typedef std::basic_string<Ch, Traits, Alloc> internal_type;
typedef E external_type;
explicit stream_translator(std::locale loc = std::locale())
: m_loc(loc)
{}
boost::optional<E> get_value(const internal_type &v) {
std::basic_istringstream<Ch, Traits, Alloc> iss(v);
iss.imbue(m_loc);
iss.setf(std::ios_base::hex, std::ios_base::basefield);
E e;
customized::extract(iss, e);
if(iss.fail() || iss.bad() || iss.get() != Traits::eof()) {
return boost::optional<E>();
}
return e;
}
boost::optional<internal_type> put_value(const E &v) {
std::basic_ostringstream<Ch, Traits, Alloc> oss;
oss.imbue(m_loc);
oss.setf(std::ios_base::hex, std::ios_base::basefield);
customized::insert(oss, v);
if(oss) {
return oss.str();
}
return boost::optional<internal_type>();
}
private:
std::locale m_loc;
};
ptree itself has get method that accepts a translator. So you can pass your own translator there.
template<class Type, class Translator>
Type get(const path_type &path,
const Type &default_value,
Translator tr) const;
Just use the new stoi/stol/stoll
with its "base" set to 0, which will do the auto detection of the numeric base.

Overloading >>operator and initiatializing using an initializer list of custom type

I've been trying to make a very simple map container and I thought it would be nice to be able to initialize it like so:
Hash table = { "name1" >> value,
"name2" >> value2,
"name3" >> value3,
"name4" >> value4 };
How I was going to go about this was by first making a pair(duo) data structure which would
keep the name and the value of each element, overload the >> operator to return a duo using the name and value parameters and make a constructor for the hash class to initialize it by using the initializer_list to construct a vector of the duo elements.Then use binary search methods to retrieve the wanted element.
Unfortunately I hit a wall from the very start.
Before I started coding everything I wanted to make sure that the overloads were used correctly and in fact it was a smart decision since apparently they are not.
This works:
#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>
struct duo {
duo(const std::string key,int value) :key(key) ,value(value) {};
const std::string key;
int value;
};
struct Hash {
std::vector<duo> lister;
Hash(std::initializer_list<duo> passed) :lister(passed) {};
};
duo operator >> (const std::string& id,int value) {
return duo(id,value);
}
int main(){
duo object1("test",1);
Hash table = {object1};
std::cout << table.lister[0].key << table.lister[0].value;
}
but this gives "invalid operands of types ‘const char [6]’ and ‘int’ to binary ‘operator>>’"
#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>
struct duo {
duo(const std::string key,int value) :key(key) ,value(value) {};
const std::string key;
int value;
};
struct Hash {
std::vector<duo> lister;
Hash(std::initializer_list<duo> passed) :lister(passed) {};
};
duo operator >> (const std::string id,int value) {
return duo(id,value);
}
int main(){
Hash table = {"test1" >> 1};
std::cout << table.lister[0].key << table.lister[0].value;
}
I tried to pass std::string to the >> operator because I can't overload with primitive types. It seems this is not a solution though. Is there any way to achieve the desired effect without explicitly converting the string literals to std::string ?
Constructing a string explicitly will resolve this:
int main(){
Hash table = {std::string("test1") >> 1};
std::cout << table.lister[0].key << table.lister[0].value;
}
I would also have >> take a reference to a const string, not a string by value:
duo operator >> (const std::string& id,int value) {
return duo(id,value);
}
You might think that you could avoid the explicit construction by devising something like:
template <size_t N> duo operator >> (const char (&ary)[N], int id)
{
return duo (std::string (ary), id);
}
...or:
duo operator >> (const char* id, int value)
{
return duo (std::string (id), value);
}
But you can't because C++ doesn't allow overriding operator >> for pointer (or any primitive) types. If you try this, you will get a compiler error along the lines of "must have an argument of class or enum type." In order to provide an operator overload, you must provide at least one argument which is of class- or enum-type.
So let's give this a bit of a think. We can't use operator<< with two primitive arguments because that's not allowed. What you really are looking for is clean initialization syntax without any explicit initialization. The only thing that's really standaing in our way is that initialization. So what if we try to construct something that actually employs two operators, but still looks clean? What if we try to construct something like:
Hash table = {"test1" <MapTo> 1};
It's not exactly the syntax you wanted, but it's fairly close. So let's try to build this:
There are two operators at work here: operator< and operator>. The arguments to operator< are the string literal "test1 and some object named MapTo. The arguments to operator> are whatever operator< returns and an integral literal, 1.
Looking at operator< first, let's hack up a prototype:
template <size_t N> std::string operator < (const char (&ary)[N], Something op)
We know that we're going to want operator> to return a duo, so what does Something need to be in order to facilitate that? All we really need operator< to do is convert the string literal to a std::string, so let's try:
class HashOperation
{
public:
template <size_t N> std::string cvt (const char (&ary)[N]) const
{
return std::string (ary);
}
} MapTo;
template <size_t N> std::string operator < (const char (&ary)[N], HashOperation op)
{
return op.cvt (ary);
}
Now we've got the std::string, let's build a duo out of that along with the int.
duo operator> (const std::string& key, int value)
{
return duo (key, value);
}
That was pretty easy. Let's put it all together (Live Demo):
#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>
struct duo {
duo(const std::string key,int value) :key(key) ,value(value) {};
template <size_t N> duo (const char (&ary)[N], int value) : key (ary), value (value) {};
const std::string key;
int value;
};
struct Hash {
std::vector<duo> lister;
Hash(std::initializer_list<duo> passed) :lister(passed) {};
};
class HashOperation
{
public:
template <size_t N> std::string cvt (const char (&ary)[N]) const
{
return std::string (ary);
}
} MapTo;
template <size_t N> std::string operator < (const char (&ary)[N], HashOperation op)
{
return op.cvt (ary);
}
duo operator> (const std::string& key, int value)
{
return duo (key, value);
}
int main(){
Hash table =
{
"test1" <MapTo> 1,
"test2" <MapTo> 2
};
std::cout << table.lister[0].key << table.lister[0].value;
}
You're trying to overload operator>> for two primitive types, char const * and int; this is not allowed by the language. To get it to work in its current form, you'll need to explicitly create an std::string from the first argument
Hash table = {std::string("test1") >> 1};
Another option, of course, is to forgo this syntax for initialization, and stick to comma separated pairs.
#include <iostream>
#include <string>
#include <vector>
#include <initializer_list>
#include <utility>
using duo = std::pair<std::string, int>;
struct Hash {
std::vector<duo> lister;
Hash(std::initializer_list<duo> passed) :lister(passed) {};
};
int main(){
Hash table = {{"test1", 1}, {"test2", 2}};
std::cout << table.lister[0].first << table.lister[0].second;
}
You defined operator>> for std::string and int, yet you try to use it with arguments const char[] and int. You need to let the compiler know that the first argument is std::string. Try
{"test1"s >> 1}

Confusion about wcout and cout, wstring and string

I have a class which holds a single wstring.
I have 2 printing functions one for ostream and one for wostream.
I must use them like this:
MyClass<char> s;
s.push_back( 'H' );
s.push_back( 'i' );
//Printing with ostream
s.print( std::cout );
MyClass<wchar_t> w;
w.push_back( L'C' );
w.push_back( L'+' );
//Printing with wostream
w.print( std::wcout );
My problem is, i cannot make a function to work with ostream, here is my class:
template<typename T>
MyClass{
private:
std::wstring *data;
public:
void push_back(const T& t) {
data->push_back(t);
}
//Does not work
void print(std::ostream& out) const{
out<<*data; //Results in a compile error
}
//Does work verry well
void print(std::wostream& out) const {
out << *data;
}
};
How can i achive this?
P.S.: I know this is nonsense, but in fact this is in a school exam which i had last year. So i cannot change the standards. This is the task.
The problem is that you're storing a std::wstring regardless of whether the template type T is char or wchar_t. Instead, you want to use the template std::basic_string with the appropriate type:
std::basic_string<T> *data;
std::string is just a typedef for std::basic_string<char>, and std::wstring is just a typedef for std::basic_string<wchar_t>.
Similarly, std::ostream is just a typedef for std::basic_ostream<char>, and std::wostream is just a typedef for std::basic_ostream<wchar_t>, so you could have a single print function that takes a std::basic_ostream templated on the appropriate type.
Store the correct type of string
std::basic_string<T> data;
and overload for just the correct basic_ostream specialisation, not for both ostream and wostream:
void print(std::basic_ostream<T> & out) const {
out << data;
}
(Also, storing the string itself rather than an uninitialised pointer will give better results).