I'm porting my game from GNU/Linux to Windows, using Visual C++.
Here is the problem:
std::stringstream sstm;
/// *working on stringstream*
const int size = sstm.str().size();
char buffer[size];
std::ofstream outfile("options", std::ofstream::binary);
for(int i = 0; i < size; i++)
buffer[i] = sstm.str().at(i);
outfile.write(buffer, size);
outfile.close();
It says: "expression must have a constant value" in declaration of buffer.
I have changed it to this:
std::vector<char>buffer(size);
And then VC says: "cannot convert parameter 1 from 'std::vector<_Ty>' to 'const char *'" at outfile.write().
const int size = sstm.str().size();
char buffer[size];
buffer is a variable length array (VLA) here. That's illegal code per C++ standard - size of an array needs to be known at compile time. VLA'a are allowed in C99 and G++ allows it as an extension in C++.
const int can be a compile time constant if it's initialiized with a literal or by a ˙constexpr. In your case, it's not.
You're almost there - vector<char> is a proper way to do it. To pass it to ostream::write() you can say buffer.data() or &buffer[0]-
You do know that sstm.str() creates a new string for each call? That will be a lot of strings, if the buffer is large.
You could get away with creating just one copy of the string:
std::stringstream sstm;
/// *working on stringstream*
std::string buffer = sstm.str();
std::ofstream outfile("options", std::ofstream::binary);
outfile.write(buffer.c_str(), buffer.size());
outfile.close();
Related
I just started learning C++, and during compilation my code I get an error:
main.cpp:59:50: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
Encryption delta("dragons.txt", "output1.txt");
I don't know what this error means, or how to make it work, so if somebody could explain to me why this is happening and how to fix that, I would be very grateful :)
#include <iostream>
#include <fstream>
using namespace std;
class Encryption
{
fstream file1; //source file
fstream file2; //destination file
public:
Encryption::Encryption(char *filename1, char *filename2)
{
file1.open(filename1, ios::in | ios::out | ios::binary);
file2.open(filename2, ios::out | ios::binary);
}
//encrypts the file
void Encrypt(void)
{
char currentByte;
bool currentBit;
int index = 0;
//sets the pointers to the beginning of the file
file1.seekg(0, ios::beg);
file2.seekp(0, ios::beg);
//reads the first value
file1.read(¤tByte, 1);
while (file1.good())
{
//loop for four bits
for (int c = 0; c < 4; c++)
{
//finds out if the first bit is a one
currentBit = (int)((unsigned char)currentByte / 128);
//shifts the byte over
currentByte <<= 1;
//if the first bit was a one then we add it to the end
if (currentBit)
{
currentByte += 1;
}
}
//writes the character
file2.write(¤tByte, 1);
//increments the pointer
file1.seekg(++index);
file2.seekp(index);
//reads the next value
file1.read(¤tByte, 1);
}
}
//closes both of the files
void close(void)
{
file1.close();
file2.close();
}
};
int main(void)
{
cout << "Welcome to the S.A.S encryption program.";
Encryption delta("dragons.txt", "output1.txt");
delta.Encrypt();
delta.close();
Encryption gamma("output1.txt", "output2.txt");
gamma.Encrypt();
gamma.close();
return 0;
}
To quote what is a literal type
Literal types are the types of constexpr variables and they can be constructed, manipulated, and returned from constexpr functions.
Essentially these are entities that can be used at compile time. Since they are constexpr, so for one thing, these are const, therefore, read-only. To fix that, you need to change to
Encryption(char const* filename1, char const *filename2)
Also, you don't need to scope the constructor of Encryption class with Encryption:: since it's defined within the class itself, so just remove that. Else your program won't compile.
Encryption::Encryption(char *filename1, char *filename2)
{
// ...
}
Since you don't intend to modify the characters which filename1, filename2 point to, you should declare them as const char *. Otherwise, informally speaking, the compiler is worried that you might do so, which would not be allowed for a string literal.
Also, since you are defining this function inside the definition of the class Encryption, you don't need to restate that with Encryption::. So change this line to
Encryption(const char *filename1, const char *filename2)
{
// ...
}
String literals are const char[] types in C++. Since C++11 onwards, string literals cannot be assigned to non-const char* pointers. Doing so would allow code to mutate a string literal's data, which is undefined behavior. This assignment was allowed prior to C++11, for backwards compatibility with C, but it was always discouraged.
If you want to pass string literals to Encryption(), you need to change the types of its parameters to const char* (or char const *) instead:
Encryption::Encryption(const char *filename1, const char *filename2)
{
file1.open(filename1, ios::in | ios::out | ios::binary);
file2.open(filename2, ios::out | ios::binary);
}
Especially since that is what fstream::open() takes in anyway, and you are not altering that parameter in any way:
void open( const char *filename,
ios_base::openmode mode = ios_base::in|ios_base::out );
As the warning tells you, converting a string constant to char* - which is not a constant - is not allowed.
The reason for this is that you can theoretically change the contents of a char*. But string constants (literals) are read-only.
Therefore you should use const char* as parameter type in your method.
My intention is to create a char[] from the length of a char* I've already created.
I asked myself why this is not valid:
void copyStringsV2() {
const char* source = "this is my string.";
const int length = strlen(source);
const char* dest[length];
}
The compiler gives me this hint:
Severity Code Description Project File Line Suppression State
Warning C4101 'str_b': unreferenced local variable CStrings
xxx\cstrings.cpp 46
Error C2131 expression did not evaluate to a constant CStrings
xxx\cstrings.cpp 161
Error (active) E0028 expression must have a constant value CStrings
xxx\CStrings.cpp 161
May you can help me out here?
You are trying to declare a variable-length array, which is not a standard feature in C++. A fixed-length array must have its length known at compile-time. That is what the compiler error is complaining about.
If you don't know the length until runtime, you will have to allocate the copied string dynamically instead, such as via new[]:
void copyStringsV2()
{
const char* source = "this is my string.";
const int length = strlen(source);
char* dest = new char[length+1];
strcpy(dest, source);
...
delete[] dest;
}
Or std::vector, so you don't need to use new[]/delete[] directly:
#include <vector>
void copyStringsV2()
{
const char* source = "this is my string.";
const int length = strlen(source);
std::vector<char> dest(length+1);
strcpy(dest.data(), source);
...
}
But, in this case, it would be better to use std::string instead:
#include <string>
void copyStringsV2()
{
const char* source = "this is my string.";
std::string dest = source;
...
}
For starters you should use the type size_t instead of the type int in this declaration
const int length = strlen(source);
^^^
This constant is a constant of the run-time. So you may not use it in the declaration
const char* dest[length];
because here is declared a variable length array and variable length arrays (VLA) is not a standard C++ feature.
Also it is unclear why the type of elements of the array is const char * instead const char.
And moreover a constant object shall be initialized.
My intention is to create a char[]
const char* dest[length];
That's not an array of char. That's an array of pointers to const char.
Also, if you want the array to be able to fit the original null termiated string, you must include the null terminator in the length of the array so that its size is length + 1.
I asked myself why this is not valid:
The compiler gives me this hint:
expression did not evaluate to a constant
May you can help me out here?
The size of an array must be a compile time constant. length is not a constant, therefore it cannot be used as length of an array.
Length of a string cannot be calculated at compile time through a pointer to element of the string. However, if you used a reference for example, and if you used a constexpr function to get the length, and used a constexpr (const works too) variable, then you could use it as the size of an array. There is such function in the C++ standard library:
auto& source = "this is my string.";
constexpr auto length = std::char_traits<char>::length(source);
char dest[length + 1];
why this is not compilable
Because this is C++, and C++ provide a wide variety of tools already1. Your best bet will be std::string. Strings can be copied and passed around with no additional code to write.
#include <string>
int main()
{
const std::string source = "this is my string.";
const std::string dest = source;
// do something with dest
}
1) So you don't need variable length arrays, which are not part of C++.
I need in C++ char array that is while initialiazing non-constant size. Size have to be non-constant cause it's generated from function and can't be used vector cause this char array will be used for reading and writing to files. Example:
int i = functionToGetvalue();
unsigned char ch[i];
file1 >> ch;
file2 << ch;
The premise is wrong. While there are reasons to prefer c-style arrays (or std::array) over vectors, yours is certainly not the one. You can certainly use std::vector to read and write to the file, since it is guaranteed to be contiguous in memory.
Example:
std::vector<char> vec;
vec.resize(255);
ssize_t sz = read(fd, vec.data(), vec.size());
In your original example, you are using formatted streams I/O, and in this case, std::string is a best tool:
std::string str;
file1 >> str; // reads up to the next white space into str
I have the following stupid snippet:
#include <iostream>
#include <cstring>
using namespace std;
int main() {
const string classpath = "Hello Dolly!";
const int len = classpath.length()+1;
char str[len];
strncpy(str, classpath.c_str(), len);
cout << str << endl;
return 0;
}
The aim ist to assign a c++ string to a c char array. The string is known at compile time, hence a constant. But the string may vary from one project to an other. I have no intention to count chars for the compiler. Hence the const len should be computed at compile time as shown. This works with cygwin and on linux. But the visualstudio compiler sees a problem and gives me the error C2131!
How to work around?
(Please consider I'm a Java programmer being troubled loosing time on such kind of problems!)
In C++ array sizes must be known at compile time.
You can dynamically allocate contiguous memory during run-time by using operator new:
char * my_array = new char[length];
Remember to use the delete[] to free up the memory after you are finished with it.
Also, C-style arrays need that terminating nul character so you will need to allocate one extra slot in the array.
To copy a std::string to a character array:
const std::string example = "example";
const std::string::size_type length = example.length();
char * p_array = new char [length + 1];
strcpy(p_array, example.c_str());
cout << p_array << endl;
delete[] p_array;
I am making use of C++ msgpack implementation. I have hit a roadblock as to how to pack binary data. In terms of binary data I have a buffer of the following type:
unsigned char* data;
The data variable points to an array which is actually an image. What I want to do is pack this using msgpack. There seems to be no example of how to actually pack binary data. From the format specification raw bytes are supported, but I am not sure how to make use of the functionality.
I tried using a vector of character pointers like the following:
msgpack::sbuffer temp_sbuffer;
std::vector<char*> vec;
msgpack::pack(temp_sbuffer, vec);
But this results in a compiler error since there is no function template for T=std::vector.
I have also simply tried the following:
msgpack::pack(temp_sbuffer, "Hello");
But this also results in a compilation error (i.e. no function template for T=const char [6]
Thus, I was hoping someone could give me advice on how to use msgpack C++ to pack binary data represented as a char array.
Josh provided a good answer but it requires the copying of byte buffers to a vector of char. I would rather minimize copying and use the buffer directly (if possible). The following is an alternative solution:
Looking through the source code and trying to determine how different data types are packed according to the specification I happened upon msgpack::packer<>::pack_raw(size_t l) and msgpack::packer<>::pack_raw_body(const char* b, size_t l). While there appears to be no documentation for these methods this is how I would described them.
msgpack::packer<>::pack_raw(size_t l): This method appends the type identification to buffer (i.e. fix raw, raw16 or raw32) as well as the size information (which is an argument for the method).
msgpack::packer<>::pack_raw_body(const char* b, size_t l): This method appends the raw data to the buffer.
The following is a simple example of how to pack a character array:
msgpack::sbuffer temp_sbuffer;
msgpack::packer<msgpack::sbuffer> packer(&temp_sbuffer);
packer.pack_raw(5); // Indicate that you are packing 5 raw bytes
packer.pack_raw_body("Hello", 5); // Pack the 5 bytes
The above example can be extended to pack any binary data. This allows one to pack directly from byte arrays/buffers without having to copy to an intermediate (i.e. a vector of char).
If you can store your image in a vector<unsigned char> instead of a raw array of unsigned char, then you can pack that vector:
#include <iostream>
#include <string>
#include <vector>
#include <msgpack.hpp>
int main()
{
std::vector<unsigned char> data;
for (unsigned i = 0; i < 10; ++i)
data.push_back(i * 2);
msgpack::sbuffer sbuf;
msgpack::pack(sbuf, data);
msgpack::unpacked msg;
msgpack::unpack(&msg, sbuf.data(), sbuf.size());
msgpack::object obj = msg.get();
std::cout << obj << std::endl;
}
Strangely, this only works for unsigned char. If you try to pack a buffer of char instead (or even an individual char), it won't compile.
MessagePack has a raw_ref type which you could use like so:
#include "msgpack.hpp"
class myClass
{
public:
msgpack::type::raw_ref r;
MSGPACK_DEFINE(r);
};
int _tmain(int argc, _TCHAR* argv[])
{
const char* str = "hello";
myClass c;
c.r.ptr = str;
c.r.size = 6;
// From here on down its just the standard MessagePack example...
msgpack::sbuffer sbuf;
msgpack::pack(sbuf, c);
msgpack::unpacked msg;
msgpack::unpack(&msg, sbuf.data(), sbuf.size());
msgpack::object o = msg.get();
myClass d;
o.convert(&d);
OutputDebugStringA(d.r.ptr);
return 0;
}
Disclaimer: I found this by poking around the header files, not through reading the non-existent documentation on serialising raw bytes, so it may not be the 'correct' way (though it was defined along with all the other 'standard' types a serialiser would want to explicitly handle).
msgpack-c has been updated after question and answers were posted.
I'd like to inform the current situation.
Since msgpack-c version 2.0.0 C-style array has been supported. See https://github.com/msgpack/msgpack-c/releases
msgpack-c can pack const char array such as "hello".
Types conversion rule is documented https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor#predefined-adaptors.
char array is mapped to STR. If you want to use BIN instead of STR, you need to wrap with msgpack::type::raw_ref.
That is packing overview.
Here are unpacking and converting description:
https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_object#conversion
Unpack means creating msgpack::object from MessagePack formatted byte stream. Convert means converting to C++ object from msgpack::object.
If MessagePack formatted data is STR, and covert target type is char array, copy data to array, and if array has extra capacity, add '\0'. If MessagePack formatted data is BIN, '\0' is not added.
Here is a code example based on the original question:
#include <msgpack.hpp>
#include <iostream>
inline
std::ostream& hex_dump(std::ostream& o, char const* p, std::size_t size ) {
o << std::hex << std::setw(2) << std::setfill('0');
while(size--) o << (static_cast<int>(*p++) & 0xff) << ' ';
return o;
}
int main() {
{
msgpack::sbuffer temp_sbuffer;
// since 2.0.0 char[] is supported.
// See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor#predefined-adaptors
msgpack::pack(temp_sbuffer, "hello");
hex_dump(std::cout, temp_sbuffer.data(), temp_sbuffer.size()) << std::endl;
// packed as STR See https://github.com/msgpack/msgpack/blob/master/spec.md
// '\0' is not packed
auto oh = msgpack::unpack(temp_sbuffer.data(), temp_sbuffer.size());
static_assert(sizeof("hello") == 6, "");
char converted[6];
converted[5] = 'x'; // to check overwriting, put NOT '\0'.
// '\0' is automatically added if char-array has enought size and MessagePack format is STR
oh.get().convert(converted);
std::cout << converted << std::endl;
}
{
msgpack::sbuffer temp_sbuffer;
// since 2.0.0 char[] is supported.
// See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_adaptor#predefined-adaptors
// packed as BIN
msgpack::pack(temp_sbuffer, msgpack::type::raw_ref("hello", 5));
hex_dump(std::cout, temp_sbuffer.data(), temp_sbuffer.size()) << std::endl;
auto oh = msgpack::unpack(temp_sbuffer.data(), temp_sbuffer.size());
static_assert(sizeof("hello") == 6, "");
char converted[7];
converted[5] = 'x';
converted[6] = '\0';
// only first 5 bytes are written if MessagePack format is BIN
oh.get().convert(converted);
std::cout << converted << std::endl;
}
}
Running Demo:
https://wandbox.org/permlink/mYJyYycfsQIwsekY