Issue building a char from sting and variables - c++

I'm trying to build a string or character to send to a function.
error:
In member function 'void RTSSHOW::RTS_Init()':
Marlin/src/lcd/e3v2/creality/LCD_RTS.cpp:383:37: warning: '%f' directive writing between 3 and 317 bytes into a region of size 0 [-Wformat-overflow=]
383 | sprintf_P((char*)mstr, PSTR("%s %f"), mstr, z_values[x][y] * 1000);
Short code summary:
auto mstr = (char*)""; // here is the string I'm trying to build
sprintf_P((char*)mstr, PSTR("%s %f"), mstr, z_values[x][y] * 1000);
sprintf_P((char*)mstr, PSTR("%s %s"), mstr, "\n");
RTS_SndData((char*)mstr, AUTO_BED_LEVEL_MESH_VP);
here is my code pastebin: https://pastebin.com/xrk2GJsh
I'm not fluent in C++ and building a string is not the same as in PHP which I am used to.
I've tried various string and char declarations, but cannot seem to get it.

Some guess work here, but I would try something like this
#include <sstream>
#include <string>
std::ostringstream mstr;
mstr << z_values[x][y] * 1000 << '\n';
RTS_SndData(mstr.str().c_str(), AUTO_BED_LEVEL_MESH_VP);
If that's not quite the correct string, no doubt you can tinker with the code.
Somewhat less flexible, but maybe less heavy is this
#include <string>
std::string mstr = std::to_string(z_values[x][y] * 1000) + "\n";
RTS_SndData(mstr.c_str(), AUTO_BED_LEVEL_MESH_VP);

Related

C++ / Arduino IDE. Publish float to MQTT - is conversion to string -> array necessary?

I am trying to publish a float to an MQTT channel in C++, within the Arduino IDE.
The following code does appear to work, but it seems to be a bit long-winded. I cobbled it together from stuff I found online. Is all this really necessary (the conversion to an array via a string), or is there a better way?
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <SparkFunBME280.h>
BME280 atmosSensor;
String tStr;
String pStr;
String hStr;
char tArr[4];
char pArr[4];
char hArr[4];
void setup() {
Setup wifi, mqtt, etc.
}
void loop() {
float tempReading = atmosSensor.readTempC();
float pressureReading = atmosSensor.readFloatPressure();
float humidityReading = atmosSensor.readFloatHumidity();
tStr = String(tempReading);
pStr = String(pressureReading);
hStr = String(humidityReading);
tStr.toCharArray(tArr, tStr.length()+1);
pStr.toCharArray(pArr, pStr.length()+1);
hStr.toCharArray(hArr, hStr.length()+1);
client.publish("atmos1/temperature", tArr);
client.publish("atmos1/pressure", pArr);
client.publish("atmos1/humidity", hArr);
}
N.B. I have pruned this code down considerably, to just the relevant bit. I'm really just asking whether the conversion to a String, and then to an array etc, is necessary.
You can use dtostrf to do the conversion in fewer steps.
char* dtostrf(double __val, signed char __width, unsigned char __prec, char * __s )
It would look something like this:
void loop()
{
float humidityReading = atmosSensor.readFloatHumidity();
constexpr size_t BUFFER_SIZE = 7; //1 char for the sign, 1 char for the decimal dot, 4 chars for the value & 1 char for null termination
char buffer[BUFFER_SIZE];
dtostrf(humidityReading, BUFFER_SIZE - 1 /*width, including the decimal dot and minus sign*/, 2 /*precision*/, buffer);
client.publish("atmos1/humidity", buffer, BUFFER_SIZE); //notice we're using the overload where you specify the length of the buffer, as we know it and it saves a call to strlen
}
A cursory glance at the MQTT source code shows that the data is stored in an internal buffer after a call to publish(), so you should be safe in reusing the same buffer for multiple calls to publish(). But be sure to check more thoroughly than I did ;)

Coding a path in unicode c++

I had a problem with opening UTF-8 path files. Path that has a UTF-8 char (like Cyrillic or Latin). I found a way to solve that with _wfopen but the way a solved it was when I encode the UTF-8 char with UTF by hand (\Uxxxx).
Is there a function, macro or anything that when I supply the string (path) it will return the Unicode??
Something like this:
https://www.branah.com/unicode-converter
I tried with MultiByteToWideChar but it returns some Hex numbers that are not relavent.
Tried:
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
std::wstring stemp = s2ws(x);
LPCWSTR result = stemp.c_str();
The result I get: 0055F7E8
Thank you in advance
Update:
I installed boost, and now I am trying to do it with boost. Can some one maybe help me out with boost.
So I have a path:
wchar_t path[100] = _T("čaćšžđ\\test.txt");
I need it converted to:
wchar_t s[100] = _T("\u010d\u0061\u0107\u0161\u017e\u0111\\test.txt");
Here's a way to convert between UTF-8 and UTF-16 on Windows, as well as showing the real values of the stored code units for both input and output:
#include <codecvt>
#include <iostream>
#include <iomanip>
#include <string>
int main() {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> convert;
std::string s = "test";
std::cout << std::hex << std::setfill('0');
std::cout << "Input `char` data: ";
for (char c : s) {
std::cout << std::setw(2) << static_cast<unsigned>(static_cast<unsigned char>(c)) << ' ';
}
std::cout << '\n';
std::wstring ws = convert.from_bytes(s);
std::cout << "Output `wchar_t` data: ";
for (wchar_t wc : ws) {
std::cout << std::setw(4) << static_cast<unsigned>(wc) << ' ';
}
std::cout << '\n';
}
Understanding the real values of the input and output is important because otherwise you may not correctly understand the transformation that you really need. For example it looks to me like there may be some confusion as to how VC++ deals with encodings, and what \Uxxxxxxxx and \uxxxx actually do in C++ source code (e.g., they don't necessarily produce UTF-8 data).
Try using code like that shown above to see what your input data really is.
To emphasize what I've written above; there are strong indications that you may not correctly understand the processing that's being done on your input, and you need to thoroughly check it.
The above program does correctly transform the UTF-8 representation of ć (U+0107) into the single 16-bit code unit 0x0107, if you replace the test string with the following:
std::string s = "\xC4\x87"; // UTF-8 representation of U+0107
The output of the program, on Windows using Visual Studio, is then:
Input char data: c4 87
Output wchar_t data: 0107
This is in contrast to if you use test strings such as:
std::string s = "ć";
Or
std::string s = "\u0107";
Which may result in the following output:
Input char data: 3f
Output wchar_t data: 003f
The problem here is that Visual Studio does not use UTF-8 as the encoding for strings without some trickery, so your request to convert from UTF-8 probably isn't what you actually need; or you do need conversion from UTF-8, but you're testing potential conversion routines using input that differs from your real input.
So I have a path: wchar_t path[100] = _T("čaćšžđ\test.txt");
I need it converted to:
wchar_t s[100] = _T("\u010d\u0061\u0107\u0161\u017e\u0111\test.txt");
Okay, so if I understand correctly, your actual problem is that the following fails:
wchar_t path[100] = _T("čaćšžđ\\test.txt");
FILE *f = _wfopen(path, L"w");
But if you instead write the string like:
wchar_t path[100] = _T("\u010d\u0061\u0107\u0161\u017e\u0111\\test.txt");
Then the _wfopen call succeeds and opens the file you want.
First of all, this has absolutely nothing to do with UTF-8. I assume you found some workaround using a char string and converting that to wchar_t and you somehow interpreted this as involving UTF-8, or something.
What encoding are you saving the source code with? Is the string L"čaćšžđ\\test.txt" actually being saved properly? Try closing the source file and reopening it. If some characters show up replaced by ?, then part of your problem is the source file encoding. In particular this is true of the default encoding used by Windows in most of North America and Western Europe: "Western European (Windows) - Codepage 1252".
You can also check the output of the following program:
#include <iomanip>
#include <iostream>
int main() {
wchar_t path[16] = L"čaćšžđ\\test.txt";
std::cout << std::hex << std::setfill('0');
for (wchar_t wc : path) {
std::cout << std::setw(4) << static_cast<unsigned>(wc) << ' ';
}
std::cout << '\n';
wchar_t s[16] = L"\u010d\u0061\u0107\u0161\u017e\u0111\\test.txt";
for (wchar_t wc : s) {
std::cout << std::setw(4) << static_cast<unsigned>(wc) << ' ';
}
std::cout << '\n';
}
Another thing you need to understand is that the \uxxxx form of writing characters, called Universal Character Names or UCNs, is not a form that you can convert strings to and from in C++. By the time you've compiled the program and it's running, i.e. by the time any code you write could be attempting to produce strings containing \uxxxx, the time when UCNs are interpreted by the compiler as different characters is long past. The only UCNs that will work are ones that are written directly in the source file.
Also, you're using _T() incorrectly. IMO You shouldn't be using TCHAR and the related macros at all, but if you do use it then you ought to use it consistently: don't mix TCHAR APIs with explicit use of the *W APIs or wchar_t. The whole point of TCHAR is to allow code to be independent and switch between those wchar_t and Microsoft's "ANSI" APIs, so using TCHAR and then hard coding an assumption that TCHAR is wchar_t defeats the entire purpose.
You should just write:
wchar_t path[100] = L"čaćšžđ\\test.txt";
Your code is Windows-specific, and you're using Visual C++. So, just use wide literals. Visual C++ supports wide strings for file stream constructors.
It's as simple as that &dash; when you don't require portability.
#include <fstream>
#include <iostream>
#include <stdlib.h>
using namespace std;
auto main() -> int
{
wchar_t const path[] = L"cacšžd/test.txt";
ifstream f( path );
int ch;
while( (ch = f.get()) != EOF )
{
cout.put( ch );
}
}
Note, however, that this code is Visual C++ specific. That's reasonable for Windows-specific code. Possibly with C++17 we will have Boost file system library adopted into the standard library, and then for conformance g++ will ideally offer the constructor used here.
The problem was that I was saving the CPP file as ANSI... I had to convert it to UTF-8. I tried this before posting but VS 2015 turns it into ANSI, I had to change it in VS so I could get it working.
I tried opening the cpp file with notepad++ and changing the encoding but when I turn on VS it automatically returns. So I was looking to Save As option but there is no encoding option. Finally i found it, in Visual Studio 2015
File -> Advanced Save Options in the Encoding dropdown change it to Unicode
One thing that is still strange to me, how did VS display the characters normally but when I opened the file in N++ there was ? (like it was supposed to be, because of ANSI)?

Handling Automatic Naming of Files In C++ Sprintf

I am currently writing a program in C++. I want to save a number of files continuously throughout the run of my program. The format of the filename is as such:
char fnameC[sizeof "C:\..._SitTurn_104_c2_00_00_000.bmp"];
- SitTurn is an experiment name
- 104 is an experiment number
These two will be changing after each different run of the program. Currently, my program works like this:
char fnameCVS[sizeof"C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_SitTurn_104_c2_02.csv"];
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
double elapsedTime;
SYSTEMTIME comptime;
int main(int argc, char *argv[])
{
GetSystemTime(&comptime);
sprintf_s(fnameCVS, "C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_SitTurn_104_c2_%02d.csv", comptime.wDay);
However, I tried this and I can't seem to get it to work. Can anyone help me?
...//rest of code set up
string expName = "SitStand";
string subjNumber = "101";
char fnameCVS[sizeof "C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_" + expName + "_" + subjNumber + "_c2_02.csv"];
LARGE_INTEGER frequency;
LARGE_INTEGER t1, t2;
double elapsedTime;
SYSTEMTIME comptime;
int main(int argc, char *argv[])
{
GetSystemTime(&comptime);
sprintf_s(fnameCVS, "C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_" + expName + "_" + subjNumber + "_c2_%02d.csv", comptime.wDay);
Since I am using this filename later in the program also, I would like to be able to just rename all files by changing the two strings: expName and subjNumber. Can someone help me explain how I can name my files using a string inputs (e.g. expName and subjNumber), so I only have to rename those corresponding string each time I change the experiment name, or subject number. Thanks!
Try this:
char fnameCVS[MAX_PATH+1];
SYSTEMTIME comptime;
GetSystemTime(&comptime);
sprintf_s(fnameCVS, _countof(fnameCVS), "C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_SitTurn_104_c2_%02d.csv", comptime.wDay);
Or this:
#include <string>
#include <sstream>
std::string expName = "SitStand";
std::string subjNumber = "101";
std::string fnameCVS;
SYSTEMTIME comptime;
GetSystemTime(&comptime);
std::ostringstream oss;
oss << "C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_" << expName << "_" << subjNumber << "_c2_" << std::setw(2) << std::setfill('0') << comptime.wDay << ".csv";
fnameCVS = oss.str();
You are mixing sprintf and std::string, which is never a good plan. You should either pick to use C's sprintf with char *, or C++'s std::string with std::stringstream.
Your fnameCVS array isn't going to be big enough: you'll take the sizeof of a std::string, which almost certainly will not be what you want.
Option 1: Use only sprintf. Allocate a big-enough string (e.g. char fnameCVS[256]) and use snprintf(fnameCVS, 256, "...Skeleton_%s_%d_c2_%02.csv", ...).
Option 2: Use only string and use a std::stringstream to build your filename.
This is a really bad idea:
char fnameCVS[sizeof"C:\\Users\\Adam\\Desktop\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_RGBDepth_DataAcquisition2013\\Skeleton_SitTurn_104_c2_02.csv"];
The main reason is that it is very difficult to visually inspect whether you have allocated the right number of bytes. Perhaps you make a slight change to the filename later in the sprintf line but then you forget to update this line or you make a typo. Boom, buffer overflow (which may go undetected until it is time to give a presentation).
A secondary bug is that when you use %02d in printf, the 2 is a minimum field width; if the number would require more than 2 digits then it outputs more than 2 digits, causing a buffer overflow. To be safe here you'd need to check that the number is between 0 and 99 before printing it.
Finally, sprintf_s is a non-standard function, there's really no reason to use it instead of sprintf or snprintf.
In C++ the equivalent formatting is a bit more wordy, but leaves no possibility of buffer overflows:
std::string fnameCVS;
// ...
std::ostringstream oss;
oss << "C:\\Users\\whatever...." << std::setw(2) << std::setfill('0')
<< comptime.wDay;
fnameCVS = oss.str();
If you really want to stick with the printf family plus a static char array (note: you can use printf and a dynamically-sized char container) then to make your code safe:
char const my_format[] = "C:\\Users\\whatever.....\\%02d.csv";
char fnameCVS[ sizeof my_format - 2 ]; // "NN" is two chars shorter than "%02d"
// ...
if ( comptime.wDay < 0 || comptime.wDay > 99 )
throw std::runtime_error("wDay out of range");
snprintf(fnameCVS, sizeof fnameCVS, my_format, comptime.wDay);
Your update indicates that you want to compute various other parts of the filename at runtime too; the C++ version that I suggest is easier to extend than the C-with-static-array version where you have to calculate the amount of memory you need by hand.

Display an int variable in a MessageBox

I am working on an old app written in Visual C++ 6.0. I am trying to display an int variable in a MessageBox for debugging reasons. Here is my code, I thought this would be a simple process, but I am just learning C++. The two lines that are commented I have tried as well with similar errors. Below is the error I am getting.
int index1 = 1;
char test1 = index1;
// char var1[] = index1;
// char *varGo1 = index1;
MessageBox(NULL, test1, "testx", MB_OK);
error C2664: 'MessageBoxA' : cannot convert parameter 2 from 'char' to 'const char *'
Why bother with C-style strings if you tagged C++?
Although Mark Ransom provided MFC solution (which is perfectly valid), here is a Standard C++ one:
int index1 = 1;
std::string test1 = std::to_string(index1);
MessageBoxA(NULL, test1.c_str(), "testx", MB_OK);
References:
std::to_string();
Arrays are evil
Use boost::format for more sophisticated formatting.
int index1 = 1;
char buf[10];
itoa(index1,buf,10);
MessageBox(NULL,buf,"Caption",MB_OK);
Can try this
CString str1;
str1.Format(_T("%d"), index1);
MessageBox(NULL, str1, "testx", MB_OK);
CString's Format works just like printf to populate the string with the parameter list.
The second parameter of MessageBox needs to be a pointer to a string of chars, terminated with NULL. Passing a char will not work.
But, learning to use a debugger is an integral part to learning a language. Why not build a debug build and set a breakpoint on char test1 = index1; instead? You do that by pressing F9 when the cursor is on that line.
For what it's worth, I prefer to use a manipulator for this:
#include <sstream>
#include <iostream>
#include <windows.h>
using std::ostringstream;
using std::ostream;
ostream &msg_box(ostream &s) {
ostringstream &os = dynamic_cast<ostringstream &>(s);
MessageBox(NULL, os.str().c_str(), "testx", MB_OK);
return s;
}
int main() {
ostringstream msg;
msg << "The number is: " << 10 << msg_box;
return 0;
}
This maintains (mostly) the same interface nearly everybody's already accustomed to with iostreams, avoids the type-unsafe CString::Format, and avoids having several lines of distraction everywhere you're going to display a little information for debugging. The other obvious good point is that if you've overloaded operator<< for your own type, that overload will work with this as well.
Acording to your error, you should declare a const pointer on the second parameter.
Like this,
const char * test1= new char();
or use
std::string test1= "";
MessageBox(NULL, test1.c_str(), "testx", MB_OK);
Also using just "Text" will work.
Here is the pure C solution using sprintf method to store all input in buffer and passing that buffer to MessageBox.
#include <stdio.h>
#include <windows.h>
int main(void)
{
int intVal = 50;
float fltVal = 5.5;
char *str = "Test String";
char buf[1024] = {'\0'};//buffer to store formatted input.
//convert formatted input into buffer.
sprintf(buf,"Int value : %d\nFloat value : %f\nString : %s\n",intVal,fltVal,str);
//display whole buffer.
MessageBox(NULL,buf,"INFO",MB_ICONINFORMATION);
return 0;
}

Creating Multiple folders with mkdir() Function using loop c++

I want to create folders in a directory by naming them in a sequence like myfolder1, myfolder2. i tried doing it with mkdir() function using a for loop but it doesn't take 'integer variables' and only takes 'const char values'. what to do now? is there any other function which do that or can mkdir() do that?
I'm not aware of any library calls that take an integer like you are asking. What you need to do is embed the number into the string before passing it to mkdir(). Since you tagged this question with 'c++' I've demonstrated a C++ oriented way of accomplishing this below.
#include <sstream> // for std::ostringstream
#include <string> // for std::string
const std::string baseFolderName = "myfolder";
for (int i = 1; i < 20; ++i)
{
std::ostringstream folderName;
folderName << baseFolderName << i;
mode_t mode = 0; //TBD: whatever is appropriate
mkdir(folderName.str().c_str(), mode);
}
If you really want this, you can use itoa(...)
Lets say
i = 20;
char buffer [33];
itoa (i,buffer,10); //10 means decimal
Now buffer = "20\0"
After this conversion you can add buffer to your default string.
So, all in all, you can use:
std::string str = "string";
char buffer[33] ;
itoa(20, buffer, 10);
str.append(buffer);
mkdir(str.c_str());