std::string -> execvp etc - c++

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.

Related

Get temp path with file name

I want to get path to file like this > %ENV%/%FILE_NAME%.docx
But c++ doesn't make sense at all and nothing works..
I would use std::string but it's not compatible so I tried multiple ways of converting it to char[] or char* but none of them works and I'm also pretty sure this is unsafe..
My code so far (I know it's the worst code ever..)
char* appendCharToCharArray(char* array, char a)
{
size_t len = strlen(array);
char* ret = new char[len + 2];
strcpy(ret, array);
ret[len] = a;
ret[len + 1] = '\0';
return ret;
}
const char* getBaseName(std::string path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension.c_str();
}
int main()
{
char szExeFileName[MAX_PATH];
GetModuleFileName(NULL, szExeFileName, MAX_PATH);
const char* file_name = getBaseName(std::string(szExeFileName));
char* new_file = getenv("temp");
new_file = appendCharToCharArray(new_file, '\\');
for (int i=0;i<sizeof(file_name)/sizeof(file_name[0]);i++)
{
new_file = appendCharToCharArray(new_file, file_name[i]);
}
new_file = appendCharToCharArray(new_file, '.');
new_file = appendCharToCharArray(new_file, 'd');
new_file = appendCharToCharArray(new_file, 'o');
new_file = appendCharToCharArray(new_file, 'c');
new_file = appendCharToCharArray(new_file, 'x');
std::cout << new_file << std::endl;
}
Using appendCharToCharArray() is just horribly inefficient in general, and also you are leaking lots of memory with the way you are using it. Just use std::string instead. And yes, you can use std::string in this code, it is perfectly "compatible" if you use it correctly.
getBaseName() is returning a char* pointer to the data of a local std::string variable that goes out of scope when the function exits, thus a dangling pointer is returned. Again, use std::string instead.
And, you should use the Win32 GetTempPath/2() function instead of getenv("temp").
Try something more like this:
#include <iostream>
#include <string>
std::string getBaseName(const std::string &path)
{
std::string base_filename = path.substr(path.find_last_of("/\\") + 1);
std::string::size_type const p(base_filename.find_last_of('.'));
std::string file_without_extension = base_filename.substr(0, p);
return file_without_extension;
}
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
std::string new_file = std::string(szTempFolder) + getBaseName(szExeFileName) + ".docx";
std::cout << new_file << std::endl;
}
Online Demo
That being said, the Win32 Shell API has functions for manipulating path strings, eg:
#include <iostream>
#include <string>
#include <windows.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
char new_file[MAX_PATH] = {};
PathCombineA(new_file, szTempFolder, PathFindFileNameA(szExeFileName));
PathRenameExtensionA(new_file, ".docx");
std::cout << new_file << std::endl;
}
Or, if you are using C++17 or later, consider using std::filesystem::path instead, eg:
#include <iostream>
#include <filesystem>
#include <windows.h>
namespace fs = std::filesystem;
int main()
{
char szExeFileName[MAX_PATH] = {};
GetModuleFileNameA(NULL, szExeFileName, MAX_PATH);
char szTempFolder[MAX_PATH] = {};
GetTempPathA(MAX_PATH, szTempFolder);
fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).stem();
new_file += ".docx";
// alternatively:
// fs::path new_file = fs::path(szTempFolder) / fs::path(szExeFileName).filename();
// new_file.replace_extension(".docx");
std::cout << new_file << std::endl;
}
Online Demo

Can't read complete string when writing into IPC SHM in c++

I'm attempting to build a simple interface to use shm ipc in c++. For that, I've written the following code:
sharedmem.h:
#pragma once
#include <iostream>
#include <sstream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
using namespace std;
namespace IPC
{
void Send(const string filename, std::string msg,int size=1024 )
{
key_t key = ftok(filename.c_str(),65);
std::stringstream ss;
ss << msg.c_str();
int shmid = shmget(key,size,0666|IPC_CREAT);
char *str = (char*) shmat(shmid,(void*)0,0);
ss >> str;
shmdt(str);
}
string Receive(const string filename, int size=1024 )
{
key_t key = ftok(filename.c_str(),65);
int shmid = shmget(key,size,0666|IPC_CREAT);
char *str = (char*) shmat(shmid,(void*)0,0);
string ret(str);
shmdt(str);
shmctl(shmid,IPC_RMID,NULL);
return ret;
}
};
Outside, I use it like:
sender.cpp
#include "sharedmem.h"
int main()
{
IPC::Send("fila1", "hello ipc");
return 0;
}
receiver.cpp
#include "sharedmem.h"
int main()
{
std::string ret = IPC::Receive("fila1");
cout << "Recebi na fila: " << ret;
return 0;
}
CMakeLists.txt:
set (CMAKE_CXX_STANDARD 17)
add_executable(sender sender.cpp)
add_executable(receiver receiver.cpp)
and built with cmake . && make
In this example I write "hello ipc" but the other process reads only "hello". What could be wrong here? Thanks in advance.
In your send function:
void Send(const string filename, std::string msg,int size=1024 )
{
key_t key = ftok(filename.c_str(),65);
std::stringstream ss;
ss << msg.c_str();
int shmid = shmget(key,size,0666|IPC_CREAT); // this call could fail, what happens next is
// a likely a segmentation error.
// ... or worse.
char *str = (char*) shmat(shmid,(void*)0,0);
ss >> str; // <-- error is here. You extract from ss until the first whitespace character.
// what happens if input string is larger than the size of the allocated block?
shmdt(str);
}
The stringstream ss has no functional use in your function, except for adding a bug. I suggest you try this instead:
int Send(const string& filename, const std::string& msg) noexcept // if you have no return value,
// you should throw on error,
// let's avoid that
{
key_t key = ftok(filename.c_str(), 65); // you should maybe consider using a named constant
// for your project ID
if (key == -1)
return errno;
int shmid = shmget(key, msg.length() + 1, 0666 | IPC_CREAT); // allocate enough memory for the
// message, plus its NULL terminator
if (shmid == -1)
return errno;
void *shared_mem = shmat(shmid, nullptr, 0);
if (shared_mem == (void*)-1)
{
// the systeml failed to lock the allocated memory.
// do some cleanup by de-allocating the shared memory block.
int ret = errno; // keep original error for return.
shmctl(shmid , IPC_RMID, nullptr);
return ret;
}
// copy message string with its NULL terminator to shared memory
memcpy(shared_mem, msg.c_str(), msg.length() + 1); // using length() + 1 is ok here, result of
// c_str() always has a NULL terminator.
shmdt(shared_mem);
return 0;
}
Your receive function also lacks in error checking. That should be very similar to the Send() function.
Note that the strings are passed by const reference, that's to avoid copying them (and the potential errors associated with those unneeded memory allocations)

Dynamically generate protobuf Message and return a pointer to it

First of all I'm not very experienced with C++, so maybe I'm overseeing something here.
I'm trying to dynamically generate protobuf Messages from .proto files with the following code:
int init_msg(const std::string & filename, protobuf::Arena* arena, protobuf::Message** new_msg){
using namespace google::protobuf;
using namespace google::protobuf::compiler;
DiskSourceTree source_tree;
source_tree.MapPath("file", filename);
MuFiErCo error_mist;
Importer imp(&source_tree, &error_mist);
printf("Lade Datei:%s \n", filename.c_str());
const FileDescriptor* f_desc = imp.Import("file");
const Descriptor* desc = f_desc->FindMessageTypeByName("TestNachricht");
const Message* new_msg_proto = dmf.GetPrototype(desc);
*new_msg = new_msg_proto->New(arena);
//Debug
cout << (*new_msg)->GetTypeName() << endl;
return 0;
}
int main(int argc, char* argv[]){
protobuf::Arena arena;
protobuf::Message *adr2, *adr1;
init_msg("schema-1.proto", &arena, &adr1);
init_msg("schema-1.proto", &arena, &adr2);
printf("MSG_Pointer: %p, %p\n", adr1, adr2);
cout << adr1->GetTypeName() << endl;
arena.Reset();
return 0;
}
I thought if i use Arena, the new Message is also available outside the scope of the function.
But there is always a segfault if i try to access the Message.
I guess it's a simple error, but I couldn't figure out, how to solve this.
Here is the ouput:
Lade Datei:schema-1.proto
packet.TestNachricht
Lade Datei:schema-1.proto
packet.TestNachricht
MSG_Pointer: 0x1b293b0, 0x1b287f0
Speicherzugriffsfehler (Speicherabzug geschrieben)
The problem, I think, is that FileDescriptor et al are destroyed when
init_msg returns, leaving the newly created message with no way to
interrogate its .proto definition. You'd need to move Importer
instance to main and keep it alive. This has nothing to do with
arenas. – Igor Tandetnik
That was the solution.
Here is some working example code
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <memory>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/arena.h>
using namespace std;
using namespace google::protobuf;
class MuFiErCo : public compiler::MultiFileErrorCollector
{
public:
void AddError(const string & filename, int line, int column, const string & message){
printf("Err: %s\n", message.c_str());
}
void AddWarning(const string & filename, int line, int column, const string & message){
printf("Warn: %s\n", message.c_str());
}
};
compiler::Importer* init_proto_dir(Arena* arena, const std::string &root_dir){
using namespace compiler;
static DiskSourceTree source_tree;
source_tree.MapPath("", root_dir);
static MuFiErCo error_mist;
static Importer* imp = Arena::Create<Importer>(arena, &source_tree, &error_mist);
return imp;
}
void init_proto_def(compiler::Importer* imp, const std::string &proto_file){
using namespace compiler;
imp->Import(proto_file);
return;
}
Message* init_msg(compiler::Importer* imp, Arena* arena, const std::string &msg_name){
const DescriptorPool* pool = imp->pool();
static DynamicMessageFactory dmf;
const Descriptor* desc = pool->FindMessageTypeByName(msg_name);
const Message* msg_proto = dmf.GetPrototype(desc);
return msg_proto->New(arena);
}
int set_value(Message* msg, const char* value_name, unsigned long int value){
const Message::Reflection* reflec = msg->GetReflection();
const Descriptor* desc = msg->GetDescriptor();
const FieldDescriptor* fdesc = desc->FindFieldByName(value_name);
reflec->SetUInt64(msg, fdesc, value);
return 0;
}
int main(int argc, char* argv[]){
Arena arena;
compiler::Importer* imp = init_proto_dir(&arena, "");
init_proto_def(imp, "schema-1.proto");
Message* msg = init_msg(imp, &arena, "packet.TestNachricht");
set_value(msg, "zahl", 23434);
cout << msg->DebugString() << endl;
return 0;
}

How do I modify strcat so that it wouldn't edit str1

Why does strcat gives me its version of str1? As far as I know there has to be & thing before paramatater in function prototype and implementation if you want to get it editted, but I don't see it here.
char *strcat( char *str1, const char *str2 );
How do I edit this function so that it would only return new string but leave out the ones I give it?
My try
char *strApp(char *dest, const char *src)
{
size_t i,j;
size_t k = 0;
for (i = 0; dest[i] != '\0'; i++);
char rdest[100];
do {
rdest[k] = dest[k];
} while(++k<=i);
for (j = 0; src[j] != '\0'; j++)
rdest[i+j] = src[j];
rdest[i+j] = '\0';
return rdest;
}
It damages second string. Could anyone give me safe and correct version? Thanks in advance.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *strApp(const char *s1, const char *s2)
{
char *pointer = malloc(strlen(s1) + strlen(s2) + 1);
if (pointer == NULL)
{
perror("failed to allocate memory");
exit(EXIT_FAILURE);
}
return strcat(strcpy(pointer, s1), s2);
}
int main()
{
char *s1 = "original";
char *s2 = " modified";
char *s3 = strApp(s1, s2);
printf("%s\n", s1);
printf("%s\n", s2);
printf("%s\n", s3);
free(s3);
return 0;
}
Just trying to point out you don't need to completely rewrite strcat() to get what you want.
strcat is, by definition, altering the target. If you don't want to, you should make a copy yourself in a target memory location you allocate yourself.
You've tagged your question with both C and C++. I'm providing a C solution. Adjustments may be needed for C++.
#include <stdlib.h>
#include <string.h>
char* strdupcat(const char* s1, const char* s2) {
size_t s1_len = strlen(s1);
size_t s2_len = strlen(s2);
char* s = malloc(s1_len + s2_len + 1);
if (s == NULL)
return NULL;
{
char* s_end = s;
s_end = mempcpy(s_end, s1, s1_len);
s_end = mempcpy(s_end, s2, s2_len);
*s_end = '\0';
}
return s;
}
Example usage:
#include <stdio.h>
int main() {
char* s = strdupcat("abc", "def");
if (s == NULL) {
perror("Can't concatenate");
return EXIT_FAILURE;
}
puts(s);
free(s);
return EXIT_SUCCESS;
}
This function is used similarly to strdup.
DESCRIPTION
The strdupcat() function returns a pointer to a new string which is a duplicate of the string s1 with a duplicate of string s2 appended. Memory for the new string is obtained with malloc(3), and can be freed with free(3).
RETURN VALUE
The strdupcat() function returns a pointer to the duplicated string, or NULL if insufficient memory was available.
ERRORS
ENOMEM Insufficient memory available to allocate the new string.
You can use strerror or perror to obtain an error message when strdupcat() returns NULL.
Here's a version that accepts an arbitrary number of arguments:
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char* strdupcatmany(int dummy, ...) {
#define strdupcatmany(...) strdupcatmany(0, __VA_ARGS__, NULL)
size_t len = 0;
char* s;
char* s_dst;
const char* s_src;
va_list ap;
va_start(ap, dummy);
while (1) {
s_src = va_arg(ap, const char*);
if (s_src == NULL)
break;
len += strlen(s_src);
}
va_end(ap);
s = malloc(len + 1);
if (s == NULL)
return NULL;
s_dst = s;
va_start(ap, dummy);
while (1) {
s_src = va_arg(ap, const char*);
if (s_src == NULL)
break;
s_dst = stpcpy(s_dst, s_src);
}
va_end(ap);
*s_dst = '\0';
return s;
}
For example,
#include <stdio.h>
int main() {
char* s = strdupcatmany("abc", "def", "ghi");
if (s == NULL) {
perror("Can't concatenate");
return EXIT_FAILURE;
}
puts(s);
free(s);
return EXIT_SUCCESS;
}
Note: I don't know how portable __VA_ARGS__ args is.

keep changing pointer internally to the class

the best way to keep a pointer in main changes reflected inside the class?
static unsigned char tmp[][20] = {"hello world", "bye world"};
class X {
unsigned char ** buffer;
public:
X(unsigned char* buff)
{
buffer = &buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
};
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(buff);
x.printThis();
memcpy(buff, tmp[1], 12);
x.printThis();
delete [] buff;
return EXIT_SUCCESS;
}
this works, but when I do the follow
buff = tmp[0];
x.printThis();
the printout doesnt print hello world again??? how to fix that
You'll need to use a pointer to pointer in your class (gulp!):
class X {
unsigned char ** buffer;
public:
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
};
And then pass in the address of the pointer during construction:
X x(&buff);
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(buff);
x.printThis();
delete [] buff;
buff = tmp[1];
x.printThis();
return EXIT_SUCCESS;
}
After you have done delete buff;, your pointer buffer in the class is pointing at memory that has been deleted, which is very bad news.
If you want to store the actual address of buff, you would need to pass the address of buff and store that, like this:
char **buffer;
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
DBG_MSG_FORMATED(".......> %s", *buffer);
}
...
X x(&buff);
Or you could make buffer a reference to buff:
char*& buffer;
X(unsigned char*& buff) : buffer(buff) {}
(No other changes needed in class or other code - but note that you can't do buffer = some_other_buffer; at a later stage - that will change the value of buff to some_other_buffer, which is probably not what you expected).
You can do something thing as bellow (using a pointer to a pointer), but sincerally, this more a problem than a solution because you are unable to delete tmp without a good care with the pointer in class X
#include <cstdio>
#include <cstring>
static unsigned char tmp[][20] = {"hello world", "bye world"};
class X {
unsigned char ** buffer;
public:
X(unsigned char** buff)
{
buffer = buff;
}
void printThis()
{
printf(".......> %s", *buffer);
}
};
int main (int argc, char * const argv[]) {
unsigned char * buff = new unsigned char[20];
memcpy(buff, tmp[0], 12);
X x(&buff);
x.printThis();
buff = NULL;
buff = tmp[1];
x.printThis();
}