Using std::string in ImGui::InputText(...) - c++

The call to ImGui::InputText() takes a char array which I need to initialise from a std::string and then transfer the contents back to the std::string. In it's simplest form:
char buf[255]{};
std::string s{"foo"};
void fn() {
strncpy( buf, s.c_str(), sizeof(buf)-1 );
ImGui::InputText( "Text", buf, sizeof(buf) );
s=buf;
}
However, it appears wasteful to have two buffers (buf and the buffer allocated within std::string) both doing much the same thing. Can I avoid the buf buffer and the copying to and from it by using just the std::string and a simple wrapper "X".
I don't care about efficiency, I just want the simplest code at the call site.
This code does work but is it safe and is there a better way?
class X {
public:
X(std::string& s) : s_{s} { s.resize(len_); }
~X() { s_.resize(strlen(s_.c_str())); }
operator char*(){ return s_.data(); }
static constexpr auto len() { return len_-1; }
private:
std::string& s_;
static constexpr auto len_=255;
};
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", X(s), X::len() );
}

If you want to use InputText() with std::string or any custom dynamic string type, see misc/cpp/imgui_stdlib.h and comments in imgui_demo.cpp.
misc/cpp/imgui_stdlib.h
namespace ImGui
{
// ImGui::InputText() with std::string
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
IMGUI_API bool InputText(const char* label, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
}
Your first code
std::string s{"foo"};
void fn() {
ImGui::InputText( "Text", &s );
}
Reading manuals works wonders.

Related

C++ Serial COM Port Access

in uPyCraft IDE or Putty, just sending km.press('a') then it works fine,
but in my C++, i tried to writefile with km.press('a'), it doesn't work.
i can't find what is wrong
uPyCraft Successfull
`bool CSerialPort::OpenPort(CString portname)
{
m_hComm = CreateFile(L"//./" + portname,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
if (m_hComm == INVALID_HANDLE_VALUE)
{
std::cout << "INVALID HANDLE" << std::endl;
return false;
}
else
return true;
}
bool CSerialPort::WriteByte(const char * bybyte)
{
byte iBytesWritten = 0;
if (WriteFile(m_hComm, &bybyte, 1, &m_iBytesWritten, NULL) == 0)
return false;
else
return true;
}
int main()
{
CSerialPort _serial;
_serial.OpenPort(L"COM4");
_serial.WriteByte("km.press('a')");
}`
i tried this,
but it doesn't work, i also check _serial Isn't INVALID HANDLE.
someone help me for sending "km.press('a')" to serial
and sending km.move(0,1) with using Putty and uPyCraft,
it works fine but
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
it doesn't work. just changing km.move(0,1) to km.move(0,10), then i don't know why but it works fine.
what is different with uPyCraft(Putty) and C++?
By the looks of it, I'm assuming your class definition looks something like this:
class CSerialPort {
public:
bool OpenPort(CString portname);
bool WriteByte(const char* bybyte);
private:
HANDLE m_hComm;
byte m_iBytesWritten;
};
byte is not the proper type. DWORD is.
CString may be used, but you are using wide string literals anyway so you could just use CreateFileW, std::wstrings and std::wstring_views.
WriteByte implies that you only want to write one byte - and indeed, your implementation does only write one byte - but it's the wrong byte. It writes one byte out of the memory of the bybyte variable, not the memory it points at.
A minor redefinition of the class:
#include <string_view> // added header
class CSerialPort {
public:
// take a `std::wstring` instead
bool OpenPort(const std::wstring& portname);
// WriteBytes instead of WriteByte:
bool WriteBytes(const void* bytesPtr, DWORD bytesToWrite);
// write both wide and non-wide string_views
bool WriteString(std::string_view str);
bool WriteString(std::wstring_view str);
private:
HANDLE m_hComm;
DWORD m_iBytesWritten; // the proper type
};
The implementation in the .cpp file then becomes:
bool CSerialPort::OpenPort(const std::wstring& portname) {
// Use CreateFileW since you've hardcoded wide string literals anyway:
m_hComm = CreateFileW((L"//./" + portname).c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);
return m_hComm != INVALID_HANDLE_VALUE;
}
bool CSerialPort::WriteBytes(const void* bytesPtr, DWORD bytesToWrite)
{
return
WriteFile(m_hComm, bytesPtr, bytesToWrite, &m_iBytesWritten, nullptr) != 0;
}
// the WriteString overloads taking string_views pass on the pointer
// and length to `WriteBytes`:
bool CSerialPort::WriteString(std::string_view str) {
return WriteBytes(str.data(), str.size());
}
bool CSerialPort::WriteString(std::wstring_view str) {
return WriteBytes(str.data(), str.size() * // wchar_t's are more than 1 byte:
sizeof(std::wstring_view::value_type));
}
And your main would then use the WriteString overload taking a std::string_view (by passing a const char* to WriteString):
int main()
{
CSerialPort _serial;
if(_serial.OpenPort(L"COM4")) {
_serial.WriteString("km.press('a')");
} else {
std::cerr << "failed opening COM4\n";
}
}
Note: The section you added at the end has several errors:
string test = "km.move(0,1)";
DWORD dwBytesWritten;
WriteFile(m_hComm,&test,sizeof(test),dwBytesWritten,NULL);
&test takes the address of the std::string object. You should use test.c_str() to get a const char* to the first character in the string.
sizeof(test) gets the size of the std::string object, not the length of the actual string. You should use test.size() instead.
dwBytesWritten is passed by value but the function expects a pointer to a DWORD that it can write to. You should use &dwBytesWritten instead.
WriteFile(m_hComm, test.c_str(), test.size(), &dwBytesWritten, NULL);

Why can I pass CHAR[] as a LPSTR parameter?

This is working for me...
std::string GetProgramDataPath() {
CHAR path[MAX_PATH];
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path); // path accepted as LPSTR parameter?
if (SUCCEEDED(hr)) {
return std::string(path); // then automatically cast to const char*?
}
else {
return std::string();
}
}
...but I don't know why. I try to pass LPSTR, but I get:
Error C4700 "uninitialized local variable 'path' used"
I look up how to initialize LPSTR and come up with this:
std::string GetProgramDataPath() {
LPSTR path = new CHAR[MAX_PATH];
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path);
if (SUCCEEDED(hr)) {
std::string strPath(path);
delete[] path;
return std::string(strPath);
}
else {
delete[] path;
return std::string();
}
}
Is this the 'correct' code? With new and delete it seems wrong. Am I doing something unsafe by just using CHAR[]? How come it works instead of LPSTR? I believe it has something to do with the "equivalence of pointers and arrays" in C, but it seems there are some automatic conversions from CHAR[] to LPSTR to const char * in this code I don't understand.
Instead of managing the memory your self with new and delete I'd use a std::string instead and let it manage the memory.
static std::string GetProgramDataPath()
{
std::string buffer(MAX_PATH, '\0');
const HRESULT result = SHGetFolderPathA
(
nullptr,
CSIDL_COMMON_APPDATA,
nullptr,
0,
buffer.data()
);
if (SUCCEEDED(result))
{
// Cut off the trailing null terminating characters.
// Doing this will allow you to append to the string
// in the position that you'd expect.
if (const auto pos{ buffer.find_first_of('\0') }; pos != std::string::npos)
buffer.resize(pos);
// Here is how you can append to the string further.
buffer.append(R"(\Some\Other\Directory)");
return buffer;
}
buffer.clear();
return buffer;
}
Here is one way you could do it using std::filesystem::path and SHGetKnownFolderPath.
namespace fs = std::filesystem;
static fs::path GetProgramDataPath()
{
struct buffer {
wchar_t* data{ nullptr };
~buffer() { CoTaskMemFree(data); }
} buf{};
const HRESULT result = SHGetKnownFolderPath
(
FOLDERID_ProgramData,
0,
nullptr,
&buf.data
);
return SUCCEEDED(result)
? fs::path{ buf.data }
: fs::path{};
}
int main()
{
fs::path path{ GetProgramDataPath() };
if (!path.empty())
{
// Here is one way you can append to a path.
// You can also use the append member function as well.
path /= R"(Some\Other\Directory)";
// When you're ready you can call either the generic_string or
// string member function on the path.
const std::string s1{ path.string() };
const std::string s2{ path.generic_string() };
// Prints: 'C:\ProgramData\Some\Other\Directory'.
std::cout << s1 << '\n';
// Prints: 'C:/ProgramData/Some/Other/Directory'.
std::cout << s2 << '\n';
}
}
This is working for me...but I don't know why.
LPSTR is just an alias for CHAR* (aka char*):
typedef CHAR *LPSTR;
In certain contexts, a fixed-sized CHAR[] (aka char[]) array will decay into a CHAR* (aka char*) pointer to its 1st element, such as when passing the array by value in a function parameter, as you are doing.
I try to pass LPSTR, but I get Error C4700 "uninitialized local variable 'path' used".
Because LPSTR is just a pointer, and you likely did not point it at anything meaningful.
Is this the 'correct' code?
Technically yes, that will work (though return std::string(strPath) should be return strPath instead). However, you should consider using std::string or std::vector<char> instead to manage memory for you, don't use new[]/delete[] directly, eg:
std::string GetProgramDataPath() {
std::vector<char> path(MAX_PATH);
HRESULT hr = SHGetFolderPathA(nullptr, CSIDL_COMMON_APPDATA, nullptr, 0, path.data());
if (SUCCEEDED(hr)) {
return std::string(path.data());
}
return std::string();
}
Am I doing something unsafe by just using CHAR[]?
No.
How come it works instead of LPSTR?
Because CHAR[] decays into the same type that LPSTR is an alias of.
it seems there are some automatic conversions from CHAR[] to LPSTR to const char * in this code.
Correct.

How to read Unicode string from process in Windows?

I'm trying to read a Unicode string from another process's memory with this code:
Function:
bool ReadWideString(const HANDLE& hProc, const std::uintptr_t& addr, std::wstring& out) {
std::array<wchar_t, maxStringLength> outStr;
auto readMemRes = ReadProcessMemory(hProc, (LPCVOID)addr,(LPVOID)&out, sizeof(out), NULL);
if (!readMemRes)
return false;
else {
out = std::wstring(outStr.data());
}
return true;
}
Call:
std::wstring name;
bool res = ReadWideString(OpenedProcessHandle, address, name);
std::wofstream test("test.txt");
test << name;
test.close();
This is working well with English letters, but when I try to read Cyrillic, it outputs nothing. I tried with std::string, but all I get is just a random junk like "EC9" instead of "Дебил".
I'm using Visual Studio 17 and the C++17 standard.
You can't read directly into the wstring the way you are doing. That will overwrite it's internal data members and corrupt surrounding memory, which would be very bad.
You are allocating a local buffer, but you are not using it for anything. Use it, eg:
bool ReadWideString(HANDLE hProc, std::uintptr_t addr, std::wstring& out) {
std::array<wchar_t, maxStringLength> outStr;
SIZE_T numRead = 0;
if (!ReadProcessMemory(hProc, reinterpret_cast<LPVOID>(addr), &outStr, sizeof(outStr), &numRead))
return false;
out.assign(outStr.data(), numRead / sizeof(wchar_t));
return true;
}
std::wstring name;
if (ReadWideString(OpenedProcessHandle, address, name)) {
std::ofstream test("test.txt", std::ios::binary);
wchar_t bom = 0xFEFF;
test.write(reinterpret_cast<char*>(&bom), sizeof(bom));
test.write(reinterpret_cast<const char*>(name.c_str()), name.size() * sizeof(wchar_t));
}
Alternatively, get rid of the local buffer and preallocate the wstring's memory buffer instead, then you can read directly into it, eg:
bool ReadWideString(HANDLE hProc, std::uintptr_t addr, std::wstring& out) {
out.resize(maxStringLength);
SIZE_T numRead = 0;
if (!ReadProcessMemory(hProc, reinterpret_cast<LPVOID>(addr), &out[0], maxStringLength * sizeof(wchar_t), &numRead)) {
out.clear();
return false;
}
out.resize(numRead / sizeof(wchar_t));
return true;
}
Or
bool ReadWideString(HANDLE hProc, std::uintptr_t addr, std::wstring& out) {
std::wstring outStr;
outStr.resize(maxStringLength);
SIZE_T numRead = 0;
if (!ReadProcessMemory(hProc, reinterpret_cast<LPVOID>(addr), &outStr[0], maxStringLength * sizeof(wchar_t), &numRead))
return false;
outStr.resize(numRead / sizeof(wchar_t));
out = std::move(outStr);
return true;
}

Sending a c++ object with vector over socket

I have for quite some time now tried to find a good way to serialize or send a state object over tcp socket. My problem is that I am not able to use any 3. party libraries like boost.
My state object contains multiple objects. The most essential is that it got some objects and a vector of objects, but no pointers (eg. probably no deep copying, if vector dont require this).
To my question: Since I cant use boost or any other libraries, what is the best way to send a object with objects over socket?
I have been thinking that I probably could make a copy constructor and send this to a stream, but I am not quite sure about the consequences of doing this.
Define (de-)serialization functions for your data types.
For example, if you have something like:
class MyClass
{
public:
int field_a;
int field_b;
std::string string;
...
};
typedef std::vector<MyClass> MyVector;
You can define the following:
void write(int fd, const MyClass &arg)
{
// either convert arg to byte array and write it, or write field by field
// here we write it field by field
write_int(fd, arg.field_a);
write_int(fd, arg.field_b);
write_string(fd, arg.string);
}
void write(int fd const MyVector &arg)
{
size_t size = arg.size();
::write(fd, &size, sizeof(size)); // beware: machine-dependent code
for (MyVector::const_iterator it = arg.begin(); it != arg.end(); it++)
{
write(*it);
}
}
Helper functions:
void write_int(int fd, int arg)
{
write(fd, &arg, sizeof(int));
}
void write_string(int fd, const std::string &str)
{
size_t len = str.length();
write(fd, &len, sizeof(len)); // string length go first
write(fd, str.data(), len); // write string data
}
And reading:
MyClass readMC(int fd)
{
// read MyClass data from stream, parse it
int f1, f2;
std::string str;
read_int(fd, f1);
read_int(fd, f2);
read_string(fd, str)
return MyClass(f1, f2, str);
}
void read(int fd, MyVector &arg)
{
size_t size;
size_t i;
read(fd, &size, sizeof(size)); // read number of elements;
arg.reserve(size);
for (i = 0; i < size; i++)
{
arg.push_back(readMC(fd));
}
}
Helper functions:
void read_int(int fd, int &res);
{
read(fd, &res, sizeof(res));
}
void read_string(int fd, std::string &string)
{
size_t len;
char *buf;
read(fd, &len, sizeof(len));
buf = new char[len];
read(fd, buf, len);
string.asssign(buf, len);
delete []buf;
}

std::string -> execvp etc

I just found an elusive bug in a program and it turned out to be because with optimization enabled, in something like the following sometimes the std::string is destroyed before processDocument() got the text out of it:
#include <stdio.h>
#include <spawn.h>
#include <string>
static void processDocument(const char* text) {
const char* const argv[] = {
"echo",
text,
NULL,
};
pid_t p;
posix_spawnp(&p, "echo", NULL, NULL, (char**) argv, environ);
}
static int mark = 'A';
static void createDocument() {
const char* vc;
std::string v = "ABCKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK42";
++mark;
v[0] = mark;
vc = v.c_str();
processDocument(vc);
}
int main() {
createDocument();
createDocument();
return(0);
}
How do I safely convert a std::string to a char* for use in execvp, posix_spawnp etc ?
I found out why it really was (here the actual minimal testcase):
std::string resultString;
const char* nodeText;
const char* altText;
resultString = "......whatever1.";
nodeText = resultString.c_str();
resultString = ".....whatever2..";
altText = resultString.c_str();
printf("%s\n", nodeText); // garbage
Bad idea.