concatenate const char * strings - c++

I'm confused about char * and const char *. In my example I'm not sure how to put them together. I have several const char * strings I would like to concatenate to a final const char * string.
struct MyException : public std::exception
{
const char *source;
int number;
const char *cause;
MyException(const char *s, int n)
: source(s), number(n) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c) {}
const char *what() const throw()
{
if (number != 0) {
char buffer[1024];
// why does this not work?
cause = strerror_r(number, buffer, 1024);
}
// how to concatenate the strings?
return source + ": " + cause;
}
};

You can store a std::string and still return a const char * from your what function.
struct MyException : public std::exception
{
private:
std::string message;
public:
MyException(const char *s, int n) {
char buffer[1024];
strerror_r(n, buffer, 1024);
message.reserve(strlen(s) + 2 + strlen(buffer));
message = s;
message += ": ";
message += buffer;
}
MyException(const char *s, const char *c) {
message.reserve(strlen(s) + 2 + strlen(c));
message = s;
message += ": ";
message += c;
}
const char *what() const throw()
{
return message.c_str();
}
};

Just use strcat() and strcpy() function from string.h.
http://www.cplusplus.com/reference/clibrary/cstring/strcat/
http://www.cplusplus.com/reference/clibrary/cstring/strcpy/
Also, since you don't have to modify original strings, the difference between const char* and char* doesn't matter.
Also don't forget to malloc() (reserve the space for) the required size of destination string.

This is how I'd implement this:
struct MyException : public std::exception
{
public:
const char *source;
int number;
const char *cause;
private:
char buffer[1024]; // #1
std::string message; // #2
std::string build_message() {
if (number != 0) {
cause = strerror_r(number, buffer, 1024); // use the member buffer
}
std::string s; // #3
s.reserve(strlen(source) + 2 + strlen(cause));
return s + source + ": " + cause;
}
public:
MyException(const char *s, int n)
: source(s), number(n), cause(), message(build_message()) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c), message(build_message()) {}
const char *what() const throw()
{
return message.c_str(); // #4
}
};
Things to note:
The original code was using a local variable for a buffer. That is a bad idea, as the pointer stored in cause would be invalid the moment the scope ends.
For the concatenated message, dynamic allocation would be required. And that also means that cleanup of that storage would be required. I grabbed an existing tool that does that and provides string-like operations: std::string.
With std::string concatenation can be done with the + operator. Note how I asked it to reserve memory for the expected size. This is memory an optimization, and is not required: the string would allocate enough memory either way.
what cannot throw an exception, otherwise a call std::unexpected would arise. So the string cannot be allocated here.

If you must work with char* pointers, you will want to use strcat. strcat takes two arguments a char* and a const char* and appends the string pointed to by the const char* onto the char*. This means you first need to copy your first string over.
You'll want to do something like this:
char* Concatenate(const char* first, const char* second)
{
char* mixed = new char[strlen(first) + strlen(second) + 2 /* for the ': ' */ + 1 /* for the NULL */];
strcpy(mixed, first);
strcat(mixed, ": ");
strcat(mixed, second);
return mixed;
}
Isn't that just ugly? And, remember, because you've dynamically allocated the char* returned by that function the caller must remember to delete[] it. This ugliness and the need to ensure the caller cleans up in the right way is why you're better off using a string implementation such as std::string.

Allocate a buffer of size strlen(source) + strlen(cause) + 3 and use sprintf to create your message. Actually you can move this code to constructor so that what becomes simple getter.

If you really must use c-strings, you should look at strcat() to concatenate them together. However, since you are creating a custom exception, it would be reasonable to consider using std::string instead because it is more friendly to use in C++.

Related

What does Copy constructor do for dynamic allocations [duplicate]

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I am very curious why is copy constructor so important for the dynamic allocation of my own defined class.
I am implementing the low-level c-string class with dynamic allocations and here is a quick view of my class
class String
{
private:
char * buf;
bool inBounds( int i )
{
return i >= 0 && i < strlen(buf);
}
static int strlen(const char *src)
{
int count = 0;
while (*(src+count))
++count;
return count;
}
static char *strcpy(char *dest, const char *src)
{
char *p = dest;
while( (*p++ = *src++));
return dest;
}
static char* strdup(const char *src)
{
char * res = new_char_array(strlen(src)+1);
strcpy(res,src);
return res;
}
static char * new_char_array(int n_bytes)
{
return new char[n_bytes];
}
static void delete_char_array( char* p)
{
delete[] p;
}
public:
/// Both constructors should construct
/// this String from the parameter s
String( const char * s = "")
{
buf = strdup(s);
}
String( String & s)
{
buf = strdup(s.buf);
}
void reverse()
{
}
void print( ostream & out )
{
out << buf;
}
~String()
{
delete_char_array(buf);
}
};
ostream & operator << ( ostream & out, String str )
{
str.print(out);
return out;
}
I know the part of strdup() function is not really correct but I am just doing some tests.
My problem is if I do not have the copy constructor and my main() is
int main()
{
String b("abc");
String a(b);
cout << b << endl;
return 0;
}
The compiler will tell me double free or corruption (fasttop) and I find some answers about this question and see the Big three rules.
Can you guys tell me why my code works without any errors if I have the copy constructor and what the error of double free or corruption (fasttop) means?
If you don't define a copy constructor, the compiler will insert one for you. This default copy constructor will simply copy all the data members, so both instances of String will point to the same area of memory. The buf variable will hold the same value in each instance.
Therefore when the instances go out of scope and are destroyed, they will both attempt to release the same area of memory, and cause an error.

Storing a string as char[] with placement new and get it back

I want to write a Class which holds information about a string in Memory and which can give it back to me. So i started with a Union which holds the size of a string. (why union doesn't matter here but it need to be union for other types lateron) The constructor get a string passed and should put the string as c_str at the end of the Objekt which i place with placement new.
The class looks like this:
class PrimitivTyp
{
public:
explicit PrimitivTyp(const std::string &s);
std::shared_ptr<std::string> getString() const;
private:
union
{
long long m_long; //use long long for string size
double m_double;
} m_data;
ptrdiff_t m_next;
};
And the impl of the Ctor and the get function looks like this which doesnt work properly i guess.
PrimitivTyp::PrimitivTyp(const std::string& s)
{
m_data.m_long = s.size();
m_next = reinterpret_cast<ptrdiff_t>(nullptr);
//calc the start ptr
auto start = reinterpret_cast<ptrdiff_t*>(this + sizeof(PrimitivTyp));
memcpy(start, s.c_str(), s.size()); //cpy the string
}
std::shared_ptr<std::string> PrimitivTyp::getString() const
{
auto string = std::make_shared<std::string>();
//get the char array
auto start = reinterpret_cast<ptrdiff_t>(this + sizeof(PrimitivTyp)); //get the start point
auto size = m_data.m_long; //get the size
string->append(start, size);//appand it
return string;//return the shared_ptr as copy
}
The Usage should be something like this:
int main(int argc, char* argv[])
{
//checking type
char buffer[100];
PrimitivTyp* typ = new(&buffer[0]) PrimitivTyp("Testing a Type");
LOG_INFO << *typ->getString();
}
This crashes and i don't find the misstake with the Debugger. I think it is something with the position calculation of this.
this + sizeof(PrimitivTyp) is not what you think, you want this + 1 or reinterpret_cast<uint8_t*>(this) + sizeof(PrimitivTyp).
Pointer arithmetic in C and C++ takes into account the type of the pointer.
so with T* t;, (t + 1) is &t[1] (assuming non overload of operator &) or reinterpret_cast<T*>(reinterpret_cast<uint8_t>(t) + sizeof(T)).

Class derived from std::exception throws invalid pointer error on what()

I tried to derive from std::exception like so:
class bad_number : public std::exception
{
public:
bad_number(const char*);
virtual const char* what() const noexcept;
private:
const char* num;
};
bad_number::bad_number(const char * num) : num(num){ }
const char* bad_number::what() const noexcept
{
std::string str;
str.append("Invalid number format:");
str.append(num);
char* result = new char[str.size()];
strcpy(result, str.c_str());
return result;
}
//The function that uses the exception
long double convert(const char *str)
{
char *endptr;
double result = strtold(str, &endptr);
if (*endptr != '\0')
throw bad_number(str);
return result;
}
The main function, in turn is the following:
int main(int argc, char ** argv)
{
std::vector<long double> vect;
for(int i = 1; i < argc; i++)
{
try{
vect.push_back(convert(argv[i]));
} catch (bad_number& e){
std::cout << e.what() << std::endl; //free():
//invalid pointer: 0x000000000131bc40 ***
}
}
}
DEMO
Why is the invalid pointer error printed? On my platform (Windows 8 + cygwin) it prints the actual what() return value. Does it mean that I have UB? If so, where in my code is UB?
In general, how can I fix that? I want a human-readable what() return value to be printed instead.
std::string::size() returns the number of characters in the string, which does not include a null terminator. Thus result is one char too short, and strcpy writes past it.
Besides, it's really a bad idea to dynamically allocate a new char array everytime what() is called. This is made worse by the fact that what() is declared noexcept, even though new may throw.
Better construct an std::string in the exception's constructor, store it as a member variable, and just return its c_str().
Modify below what function code like this
const char* bad_number::what() const noexcept
{
std::string str;
str.append("Invalid number format:");
str.append(num);
char* result = new char[str.size()+1];
strcpy(result, str.c_str());
return result;
}

C++ & the C arg_list functions

I need to emulate a library type that allows treating it both as char* & std::string. e.g. this should be allowed:
char c[16];
MyString ms;
...
strcpy(c, ms);
strcpy(c, ms.c_str());
I only control the library but unfortunately can't change this library usage. I cannot change the printf call, just the type sent to it!
I can do this for instance by implementing:
class MyString : public string {
public:
operator const char*();
};
The problem is with this:
sprintf(c, "%s", ms);
The C "arg_list" functions will not use the char* cast.
Any suggestions?
Thanks.
You can pass only trivially-copyable PODs to ....
You could do
struct wrapper { const char * it; };
wrapper it{"hello"};
printf("%s", it);
But the problem is, you cannot manage memory. Once you try to add constructor and destructor to alloc/free the memory, you will be faced with an error.
error: cannot pass objects of non-trivially-copyable type ‘class mystr’ through ‘...’
You can help yourself with:
const char * c_str(const char * it) { return it; }
const char * c_str(const std::string& it) { return it.c_str(); }
const char * cstr = "hello";
std::string cppstr = "world";
printf("%s %s!", c_str(cstr), c_str(cppstr));
Or use boost::format (or similar for ostream). And you should use ostream/format for C++ (type-safe).
You said:
The problem is with this:
sprintf(c, "%s", ms);
The C "arg_list" functions will not use the
char* cast.
There is no cast here. sprintf(..., "%s") is not casting ms to anything, so it will never call your conversion operator. A cast would be: sprintf(c, "%s", (const char *)ms). Unlike strcpy, va_list doesn't cause a coercion as there is no parameter type.
You can pass objects to va_list functions, but it you need to either explicitly cast the arguments to call the correct type conversion, or use .c_str()
If printf() could implicitly handle C++ objects per its format specifiers, it would certainly already be in the language, since it is probably one of the most desired library functions that didn't map over from C.
If you are set on using printf() and family, simply write:
printf("%s", foo.c_str());
Or try something like this (as suggested by #firda in a comment):
const char * c_str(const std:string &s) { return s.c_str(); }
const char * c_str(const char * s) { return s; }
Then you consistently write:
printf("%s%s", c_str(foo), c_str(bar));
Normal C++ variadic functions cannot work with object instances because the values must be bit-copiable (the called function will not know the types of the objects passed at compile time).
With C++11 you can however write a variadic template that takes care of doing the trick of using the call-site type knowledge to unpack the parameters:
#include <string>
#include <stdio.h>
#include <string.h>
int mysprintf(char *buf, const char *fmt) {
return sprintf(buf, "%s", fmt);
}
template<typename T, typename... Args>
int mysprintf(char *buf, const char *fmt, const T& x, Args... args) {
const char *sp = strchr(fmt, '%');
if (sp) {
while (fmt < sp) *buf++ = *fmt++;
std::string fmt1(fmt, fmt+2);
int sz = sprintf(buf, fmt1.c_str(), x);
return sz + mysprintf(buf+sz, fmt+2, args...);
} else {
fputs("Invalid format string\n", stderr);
std::abort();
return 0;
}
}
template<typename... Args>
int mysprintf(char *buf, const char *fmt, const std::string& x, Args... args) {
const char *sp = strchr(fmt, '%');
if (sp) {
if (sp[1] != 's') {
fputs("Invalid format string (%s expected)\n", stderr);
std::abort();
}
while (fmt < sp) *buf++ = *fmt++;
std::string fmt1(fmt, fmt+2);
int sz = sprintf(buf, fmt1.c_str(), x.c_str());
return sz + mysprintf(buf+sz, fmt+2, args...);
} else {
fputs("Invalid format string\n", stderr);
std::abort();
return 0;
}
}
int main() {
char buf[200];
std::string foo = "foo";
const char *bar = "bar";
int x = 42;
mysprintf(buf, "foo='%s', bar='%s', x=%i", foo, bar, x);
printf("buf = \"%s\"\n", buf);
return 0;
}
NOTE: to keep things simple this code doesn't handle any formatting options or % escaping, but allows using %s for both naked char * and std::string.
NOTE2: I'm not saying this is a good idea... just that's possible

How do I capture a smart pointer in a lambda?

What is best way to capture a smart pointer in a lambda? One attempt of mine lead to a use-after-free bug.
Example code:
#include <cstring>
#include <functional>
#include <memory>
#include <iostream>
std::function<const char *(const char *)> test(const char *input);
int main()
{
std::cout.sync_with_stdio(false);
std::function<const char *(const char *)> a = test("I love you");
const char *c;
while ((c = a(" "))){
std::cout << c << std::endl;
}
return 0;
}
std::function<const char *(const char *)> test(const char *input)
{
char* stored = strdup(input);
char *tmpstorage = nullptr;
std::shared_ptr<char> pointer = std::shared_ptr<char>(stored, free);
return [=](const char * delim) mutable -> const char *
{
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
};
}
fails, as shown by AddressSanitizer.
A lambda (even one with a universal capture like [=]) only actually captures variables used within its definition. Since in your example, pointer is never used inside the lambda, it's not captured and thus when it goes out of scope, it's the last shared pointer referring to stored and free() is called.
If you want to capture pointer, you could force its use:
return [=](const char * delim) mutable -> const char *
{
pointer;
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
};
However, this is rather hackish. You want your functor stateful and with nontrivial state management. To me, this is a strong indicator an actual named class (instead of a lambda) would be in order. So I would change it like this:
std::function<const char *(const char *)> test(const char *input)
{
struct Tokenizer
{
std::shared_ptr<char> pointer;
char* stored;
char* tmpstorage;
explicit Tokenizer(char* stored) : pointer(stored, free), stored(stored), tmpstorage(nullptr) {}
const char* operator() (const char * delim)
{
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
}
};
return Tokenizer(strdup(input));
}
Just capture the variable by value and let the copy constructor and destructor worry about ownership semantics- that's what smart pointers are for.