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).
Related
I have a template function, within which I need to convert a const char * to my template value.
I know for a fact that this const char * was originally read from a ascii text file. My current code looks like this:
template <typename T>
bool Get(T &value, std::string const &query, T const &default)
{
const char* result = DataHandler.GetValue(query);
if (result != NULL)
{
value = static_cast<T>(result); //Here is the issue
return true;
}
value = default
return false;
}
Using this with an int as example i get the error
error C2440: 'static_cast' : cannot convert from 'const char *' to 'int'
Is there a way I can convert the char* to my type T seamlessly, I could not find an answer for this on SO.
In the worst case I can make a case for the 10 types I expect and give an error if not one of these, but I would rather not do it that way if possible.
Is there a way I can convert the char* to my type T seamlessly,
No.
There is no way to make conversion from string to a type automatically work with all types. Such conversion has to be implemented for each class. Typically, it is done by implementing the stream extraction operator >> for std::istream. The built in types such as int and some standard types such as std::string already have such operator. Then you can do for example:
std::istringstream istream(result);
int i;
istream >> i;
Without knowing exactly what you are asking for, using the answer given by #eerorika, here is a generic way to do the conversion:
#include <sstream>
#include <string>
#include <iostream>
template <typename T>
struct DefaultConverter
{
static T Convert(const char *result)
{
std::istringstream istream(result);
T i;
istream >> i;
return i;
}
};
template <typename T, typename Converter = DefaultConverter<T>>
bool Get(T &value, std::string const &query, T const &defaultVal)
{
const char* result = "100";
value = Converter::Convert(result);
return true;
}
int main()
{
int test = 10;
Get(test, "abc123", test);
std::cout << test;
}
The code referring to the DataHandler function is not important to illustrate what the above does.
Basically, the Get function takes an additional argument, namely a conversion template that has an available Convert function that can be called by Get. Note that the default converter simply uses the code as illustrated in the previous answer given by #eerorika.
This gives you the flexibility of providing your own converter that has a Convert function that can do anything customized.
For example:
struct SomeMPEGObject
{
SomeMPEGObject() {}
SomeMPEGObject(const char *) {}
};
struct MPEGConverter
{
static SomeMPEGObject Convert(const char *result)
{
return SomeMPEGObject(result);
}
};
//...
SomeMPEGObject mpeg;
Get<SomeMPEGObject, MPEGConverter>(mpeg, "12345", mpeg);
This will work without changing any code in the Get function.
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.
I have C++ class similar to this:
class A{
std::string str;
public:
A(std::string &str) : str(str){}
int cmpAt(const std::string &key) const{
return str.cmp(key);
}
int cmpAt(const char *key) const{
return str.cmp(key);
}
}
Both cmpAt methods look the same. Is there some way NOT to duplicate the method? Perhaps with template ?
You should just write a function template:
template <typename K>
int cmpAt(K const& key) const {
return str.compare(key);
}
That way, if you call cmpAt with a const char*, you can avoid the overhead of having to construct an extra std::string.
EDIT Never mind, you're out of luck:
int compare(const charT* s) const;
5 Returns: compare(basic_string(s)).
So just delete the const char* overload - it provides you nothing of extra value over the std::string const& overload. You would have to write your own compare function to avoid the extra string constructor, at which point it's not code duplication anymore.
I would use boost::string_ref or your favourite other implementation of it
class A{
std::string str;
public:
A(std::string &str) : str(str){}
int cmpAt(const boost::string_ref &key) const{
return key.compare(str) * -1;
}
}
This will not create temporary strings and you can pass string literals and std::string.
When int cmpAt(const std::string &key) is called with const char*, key will be constructed with const char*. so you can simply delete cmpAt(const char *key).
As others have correctly pointed out, in your specific case, there's no need for a non-const comparison function.
However, in the general case, you can do the following:
RetType someMethod(params...) const {
return LongAndComplexComputation(params, ...);
}
// Return type deduction is C++14.
// If you can't use C++14, only C++11, the return type should be:
// const std::remove_reference<decltype(*this)>::type *
auto cthis() const {
return this;
}
RetType someMethod(params...) {
return cthis()->someMethod(params, ...)
}
If necessary, you will have to cast away const-qualification from the return type (e.g. when you are returning a pointer inside *this), using const_cast.
Error
stack.cc:53:28: error: no matching function for call to ‘Stack<std::basic_string<char> >::push(std::string)’
stack.cc:53:28: note: candidate is:
stack.cc:32:11: note: Stack<T>& Stack<T>::push(T&) [with T = std::basic_string<char>]
stack.cc
#include<iostream>
template <typename T>
class Stack {
private:
T* array_;
int length_;
T* last_;
void expandArray();
public:
Stack(int length = 8) {
array_ = new T[length];
length_ = length;
last_ = array_;
}
Stack<T>& push(T&);
T pop();
};
template<typename T>
void Stack<T>::expandArray() {
T* array_temp = new T[length_ << 1];
memcpy(array_temp, array_, length_);
std::swap(array_, array_temp);
delete[] array_temp;
length_ <<= 1;
}
template<typename T>
Stack<T>& Stack<T>::push(T& data) {
if (last_ == (array_ + length_ - 1)) {
expandArray();
}
last_[0] = data;
last_++;
return *this;
}
template<typename T>
T Stack<T>::pop() {
if(array_ != last_) {
T temp = last_[0];
last_--;
return temp;
}
return NULL;
}
int main() {
Stack<std::string> s;
s.push(std::string("a"))
.push(std::string("b"))
.push(std::string("c"))
.push(std::string("d"));
std::cout << s.pop() << std::endl;
std::cout << s.pop() << std::endl;
std::cout << s.pop() << std::endl;
std::cout << s.pop() << std::endl;
}
Wanted to understand why a conversion is happening from std::string to std::basic_string<char>?
Please feel free to comment on the code quality as well.
The actual problem is that you're attempting to pass a reference to an object created in the argument rather than stored in a variable. See that T& argument to Stack<T>::push? You cannot pass your temporary here because it's a non-const reference. Try as follows...
Stack<T>& push(const T&);
Use this signature for your implementation, too.
That being said, know that std::string is a mere typedef over std::basic_string<char>. This is because the functionality for strings can be extended to the other character types, too -- std::wstring for wchar_t, std::u16string for char16_t, and std::u32string for char32_t. ;-)
See §21.4 Class template basic_string [basic.string] of the C++11 specification.
// basic_string typedef names
typedef basic_string<char> string;
typedef basic_string<char16_t> u16string;
typedef basic_string<char32_t> u32string;
typedef basic_string<wchar_t> wstring;
Note that you shouldn't rely on iostream to include string for you. You should also include cstring and specify the scope for your memcpy call.
By the way, you should really look into using an initializer list for Stack's constructor... see as follows.
Stack(int length = 8) : length_(length), array_(new T[length]), last_(array_) { }
Note that this functioning properly on array_ preceding last_ as a class member declaration ;-)
... AND a final note. Your pop is incorrect, since you're returning the element past the top. Instead, try something as follows...
template<typename T>
T Stack<T>::pop() {
if (array_ != last_) {
return *--last_;
}
/* other stuff here */
}
You need to decrement prior to dereferencing, since last_ points past the top. As a side note, you're also returning a copy of the std::string from pop, even though you stated you wanted to avoid as such.
Note you shouldn't be returning NULL since this isn't a pointer type. In fact, you'll just be creating an std::string via the constructor that takes a const char *... which is explicitly prohibited in the case of NULL. See §21.4.2¶8-9...
basic_string(const charT* s, const Allocator& a = Allocator());
Requires: s shall not be a null pointer.
std::string is a typedef for std::basic_string<char> it is not being converted to it, it is it. The reason is that the same std::basic_string template can be used with wchar_t to have unicode strings.
Because that is what std::string really is - it is just an alias for `std::basic_string':
typedef basic_string<char> string;
When the compiler is reporting the error message, it is displaying the typedef's real base type, not the alias name.
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"));
}
}