6

I need a way to get this PHP behaviour in C++:

$foo = "PHP";
$bar = "this is a " . $foo . " example.";

Is there something close to that, or do I have to do lots of strcat?

emi
  • 697
  • 1
  • 6
  • 19
  • strcat is C(++). If you are using C++, you can use std::string and go ahead and do str = "something" + str + "something"; – Etherealone May 15 '13 at 22:31
  • 2
    Beware with @Tolga comment above, as it will work if at least one of the arguments to the addition is a `std::string`, but will fail to concatenate string literals. Sadly (or luckily, depends on who you ask), string literals in C++ are not `std::string`, but arrays of constant characters. – David Rodríguez - dribeas May 15 '13 at 23:38

6 Answers6

18

Easy enough with std::string:

std::string foo = "C++";
auto bar = "this is a " + foo + " example.";

Just make sure one of the first two operands is a std::string, not both const char * or something.


As noted below, this result is being used in CreateProcess as a char * (LPSTR) argument. If the argument was const char *, c_str() would be perfectly acceptable to pass in. However, it is not, which means you should assume it modifies the string. MSDN says this:

The Unicode version of this function, CreateProcessW, can modify the contents of this string.

Since this is char *, it's evidently using CreateProcessA, so I'd say a const_cast<char *> should work, but it's better to be safe.

You have two main options, one for C++11 and later, and one for pre-C++11.

C++11

std::string's internal buffer is now guaranteed to be contiguous. It's also guaranteed to be null-terminated. That means you can pass a pointer to the first element:

CreateProcess(..., &str[0], ...);

Make sure the function only overwrites indices within [0, size()) in the internal array. Overwriting the guaranteed null-terminator is not good.

C++03

std::string is not guaranteed to be contiguous or null-terminated. I find it best to make a temporary std::vector, which guarantees the contiguous part, and pass a pointer to its buffer:

std::vector<char> strTemp(str.begin(), str.end());
strTemp.push_back('\0');
CreateProcess(..., &strTemp[0], ...);

Also note MSDN again:

The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.

That seems to suggest that the null-terminator here isn't necessary, but there's no size parameter, so I'm not completely sure.

chris
  • 60,560
  • 13
  • 143
  • 205
  • Does that atually work, don't you need `std::string("this is a")` to ensure it's a string? – Mats Petersson May 15 '13 at 22:34
  • 2
    @MatsPetersson, [Yes, it sure does.](http://coliru.stacked-crooked.com/view?id=ba67d0382272cb5f71a7c5b251948d96-e54ee7a04e4b807da0930236d4cc94dc) `operator+(const char *, operator+(const std::string &, const char *))`. The inner returns a `std::string`. – chris May 15 '13 at 22:41
  • @MatsPetersson, Dang it, my first was right. It goes left to right, which means the result of `"this is a " + foo` is the first argument to `result + " example."`. I'm just hopelessly confusing myself. – chris May 15 '13 at 22:44
  • What if now I get `cannot convert 'std::string' to 'LPSTR'` when I use the concatenated string? I've tried converting with c_str() but then I get `invalid conversion from 'const char*' to 'LPSTR'`. – emi May 15 '13 at 22:48
  • @esauvisky : That is a slightly different matter. What is the actual function you are passing it to - does it actually change the string? Otherwise, using a cast (`static_cast(somestring.c_str())` should work - but don't use that if the string is being modified by the API function). – Mats Petersson May 15 '13 at 22:50
  • It's a CreateProcess function. The concatenated string is the second argument of the function, the process' parameters. – emi May 15 '13 at 22:53
  • @esauvisky, If you use C++11, you can pass in `&str[0]` to pass a modifiable character buffer, as long as it only overwrites up to `length()` characters from the beginning. Otherwise, try making a `std::vector temp(str.begin(), str.end())` and passing `&tmp[0]`. Make sure it's null-terminated if needed. – chris May 15 '13 at 22:54
  • Oh, crap, I didn't understand anything of what you've said. – emi May 15 '13 at 22:57
  • OH, wait! I've solved it somehow with this: `#define text(text) ((char*)std::string(text).c_str())` – emi May 15 '13 at 22:58
  • @esauvisky, That's extremely bad if the function modifies it. The `&str[0]` takes the address of the reference returned by `std::string`'s subscripting operator. The pointer from taking the address is treated like an array of characters by the function, as they are sequential in memory. The `std::vector` option uses the iterator pair constructor to make it with the string's contents, and then does the same thing. `std::string`'s internal buffer is not guaranteed to be contiguous in memory before C++11, though, so that solution should be saved for C++11. `#define` is frowned upon in general. – chris May 15 '13 at 22:58
  • @chris =( what do you suggest then? My problem now is: I have a `string` variable that needs to be passed as CreateProcess 2nd argument and apparently it only takes `LPSTR` variables. what should I actually do? it used to work when the parameters were simply `char *`... – emi May 15 '13 at 23:04
  • @esauvisky, I'll just add it to my answer. – chris May 15 '13 at 23:05
  • Oh, now I get it! Thanks!! – emi May 15 '13 at 23:23
  • I always find it interesting how people tend to use `auto` everywhere, except that it does not work as expected. You then end up with code in which the first line explicitly states that the object is `std::string`, but the second one (which cannot be any other thing than `std::string`) is `auto`... – David Rodríguez - dribeas May 15 '13 at 23:41
  • @DavidRodríguez-dribeas, I kind of figured it would be more familiar to a PHP developer, though trying to use C++ like PHP is futile anyway. I tend not to use it everywhere, but more when I don't particularly care about the type or when it's obvious what it is. – chris May 15 '13 at 23:47
  • @chris: It is just a pet peeve of mine... I like most types to be clearly stated, unless you don't know, don't want to know or don't care what the types are. Yet I find multiple places where people just drop `auto` anywhere (funny enough some of the people that before complained about the imprecise `var` in other languages). I actually discussed this with Stroustrup in a presentation, where he had: `auto x = std::async(f,5);` which, without seeing the declaration of `f` is unclear, while the original author (him) knew that it was `std::future x =...`. I think it hurts maintainability. – David Rodríguez - dribeas May 15 '13 at 23:52
  • ... admitedly in this case you don't need to type `std::string`, as it is clear from the context that it has to be that. But in this case I had to pause for half a second to notice why `auto` was not used in both lines (why only the second? well, 'cos in the first line it won't work, duh!) – David Rodríguez - dribeas May 15 '13 at 23:54
  • @DavidRodríguez-dribeas, I agree to some extent (more so than not). Resharper (a VS extension for C#) even has a warning you can turn on to use `var` instead of the actual type name wherever possible. `auto` could be especially useful for `std::async` in the case that you don't use the result, or use it for one thing where knowing the type is unimportant, because of that gotcha with `std::async` launching a new thread or not. I didn't mention that I like it for long (usually iterator) type names as well, though my "long" is probably not long in other people's eyes. – chris May 16 '13 at 00:14
  • @DavidRodríguez-dribeas, The main thing is that up to now, I've only really programmed for myself, so I know that I'm most likely the only one reading the code, and anything obvious to me now about specifics of C++ will most likely be obvious later as well. That definitely changes as soon as you have to consider how many other people working with your code, and I should think about that more often when writing. – chris May 16 '13 at 00:18
  • @chris: I understand what *long* is... considering that my company has a hard line in the 79th column (not 80, not 78, 79 is the *right* number). Things turn into multiline statements very easily... and we cannot use C++11 yet, so we end up with local `typedef`s more often than not. – David Rodríguez - dribeas May 16 '13 at 00:18
  • It is painful but doable... you can see how it ends [here](https://github.com/bloomberg/bsl/tree/master/groups/bsl), our implementation of non-standard standard C++03 containers is inside `bslstl`. The formatting rules are strict, and they do require a good amount of effort (types/names of arguments and members are aligned inside each block, for example) – David Rodríguez - dribeas May 16 '13 at 00:36
  • The comments aren't for extended discussion (as the system alludes to when it pops up that item). If you want something kept around for eternity, edit it into the question (if it's a clarification), or post it as an answer (if it is that). If you're just looking to chat, chat is [over here](http://chat.stackoverflow.com). – George Stocker May 16 '13 at 12:53
  • @GeorgeStocker, Sorry, I didn't realize it got that long and off-topic. – chris May 16 '13 at 13:22
  • "It's also guaranteed to be null-terminated" Where does the standard guarantee that `std::string` buffer is null-terminated? – Aykhan Hagverdili Sep 16 '19 at 08:25
  • @Ayxan, I don't know that it's more explicit elsewhere, but [\[string.accessors\]](http://eel.is/c++draft/string.accessors) requires `c_str()` and `data()` to return a pointer `p` where `p + str.size() == addressof(str[str.size()])`, where `str[str.size()]` is specified to be a value-initialized character (`'\0'`) in [\[string.access\]](http://eel.is/c++draft/string.access). This along with the address equality requirement for all other indices means that `&str[0]` must also be a pointer to a null-terminated string (barring pedantic things like a custom char type with overloaded `operator&`). – chris Sep 16 '19 at 09:32
  • As I say that, I find [this bit](http://eel.is/c++draft/basic.string#3), which is more clear. – chris Sep 16 '19 at 09:37
  • @chris if you call `c_str()` or `data()`, sure, it is null terminated until you modify the string. But you don't call either of those in your answer. I am not sure it is required for implementations to always keep `std::string` internal buffer null terminated. – Aykhan Hagverdili Sep 16 '19 at 12:00
  • @Ayxan, I use `&str[0]` and I already said the pointer you get back from `c_str` and `data` is the same as `&str[0]` per the specification of those two functions. The only option left for a wacky implementation is to null-terminate the buffer when you call `c_str` or `data`, but those functions are `const`. (Nowadays, there's also a non-const `data`, but this wasn't the case in C++11.) – chris Sep 16 '19 at 18:42
  • @chris how is it relevant that those functions are `const`? `std::string` almost always holds a *pointer* to actual data, which means the data can be modified in a `const` function. If you are not calling `c_str` or `data`, I don't think the standard guarantees that the string is null terminated. The statement in your answer seems to be not correct. – Aykhan Hagverdili Sep 16 '19 at 19:00
  • @Ayxan, Fair point on the pointer being const. In truth, I'm pretty sure that when I wrote this answer, I took the null-termination guarantee from [this question](https://stackoverflow.com/questions/12740403/legal-to-overwrite-stdstrings-null-terminator), which is probably more relevant to the issue than here. As far as I can tell, it would be technically legal to keep an extra element for the null terminator at all times, but modify that element to the null-terminator only when required. Then again, [this](http://eel.is/c++draft/requirements#res.on.data.races-3) might prevent even that. – chris Sep 16 '19 at 19:36
8

Yes, you can use std::string:

std::string foo = "PHP";
std::string bar = std::string("This is a") + foo + std::string(" example.")
taocp
  • 23,276
  • 10
  • 49
  • 62
6

In C++, you can use std::string:

std::string foo = "C++"
std::string bar = std::string("this is a") + foo + " example.";

You need the std::string(...) to make the first string into a std::string, since otherwise it's a const char *, which doesn't have operator+ to join it with string.

There are probably at least 5 other possible ways to do this, like almost always in C++.

[Again being too slow in my typing]

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
3

C++ provides the string class.

string foo = "PHP";
string bar = string("this is a ") + foo + string(" example.");
paddy
  • 60,864
  • 6
  • 61
  • 103
  • a number can be used into that constructor of string? – gumuruh Jan 27 '22 at 06:59
  • @gumuruh What do you mean? There are string constructors that take integral values, but none of them will convert a "number" to a string. I get the feeling you're looking for something like [std::to_string](https://en.cppreference.com/w/cpp/string/basic_string/to_string) – paddy Jan 28 '22 at 04:10
  • ah, yeah... you're right... we need to convert a number std::to_string() if they're located (concatenated) in the middle of string variables. thanks @paddy. – gumuruh Feb 03 '22 at 06:48
3

If you are using C++ with the standard C++ library, you can use a std::stringstream to accomplish that. The code would look something like this:

#include <sstream>

std::string const foo("C++");
std::stringstream bar;

bar << "this is a " << foo << " example";

std::string const result(bar.str());

If for some reason you cannot use the C++ standard library you are unfortunately stuck with the likes of strcat.

yas17
  • 401
  • 1
  • 3
  • 14
Timo Geusch
  • 24,095
  • 5
  • 52
  • 70
3

You can use std::string for this. So try this:

#include <string>

int main() {
    std::string foo = "C++";
    std::string bar = "this is a " + foo + " example.";
    return 0;
}
ecdeveloper
  • 2,777
  • 1
  • 22
  • 16