Related
I've got a function which takes a string, let's call it
void print(std::string myString)
{
std::cout << myString;
}
I want to do something like
char myChar;
myChar = '{';
print("Error, unexpected char: " + myChar + "\n");
It doesn't work.
I tried something like
print(std::string("Error, unexpected char") + std::string(myChar) + std::string("\n) )
but then std::string(myChar) becomes whatever int the char represents, it's printed as an int and isn't printed as it's alphanumeric representation!
The function should be declared like:
void print( const std::string &myString)
{
std::cout << myString;
}
and called like:
print( std::string( "Error, unexpected char: " ) + myChar + "\n");
As for your comment:
as a follow up, would it have been possible to pass an anonymous
function returning a string as an argument to print()? Something like
print( {return "hello world";}
then you can do this as it is shown in the demonstration program:
#include <iostream>
#include <string>
void f( std::string h() )
{
std::cout << h() << '\n';
}
int main()
{
f( []()->std::string { return "Hello World!"; } );
return 0;
}
If you are using C++14, you can do this:
using namespace std::literals;
char myChar;
myChar = '{';
print("Error, unexpected char: "s + myChar + "\n"s);
You can convert any one and concat.
You can use str.c_str() to convert C++ string to C character array.
Or
Use std::string inbuilt constructor to convert C character array to C++ string.
I've tried to convert an integer to a hex null-terminated (or "C-style") string but I cannot use it with printf or my custom log function. It only works if I convert it to an std::string then use .c_str() when passing it as a parameter, which produces ugly, hard-to-understand code.
It's important to know that using std::string and appending to it with "str +=" does work.
const char* IntToHexString(int nDecimalNumber) {
int nTemp = 0;
char szHex[128] = { 0 };
char hex[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
while (nDecimalNumber > 0) {
nTemp = nDecimalNumber % 16;
sprintf(szHex, "%s%s", hex[nTemp], szHex);
nDecimalNumber = nDecimalNumber / 16;
}
sprintf(szHex, "0x%s", szHex);
return szHex;
}
I've tried to use Visual Studio Debugger but it doesn't show any error messages, because crashes somewhere in a DLL that has no symbols loaded
Your main problem is that you define a variable on the stack, locally in the function, and then return it.
After the function returns, the char* will point to "somewhere", to an undefined position. That is a major bug. You have also other bugs that have been commented on already. Like sprintf(szHex, "0x%s", szHex);, which is undefined behaviour (UB) or sprintf(szHex, "%s%s", hex[nTemp], szHex); which has the same problem + additionally a wrong format string.
The more C++ solution would be, as already shown in many posts:
#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>
std::string toHexString(unsigned int hexValue)
{
std::ostringstream oss;
oss << "0x" << std::hex << hexValue;
return std::string(oss.str());
}
int main()
{
std::cout << toHexString(15) << '\n';
// or directly
std::cout << "0x" << std::hex << 15 << '\n';
return 0;
}
Of course a C-Style solution is also possible.
But all the following I would not recommend:
If you want to stick to C like solution with char *, you could make the char szHex[128] = { 0 }; static. Or, even better, pass in the pointer to a buffer and return its address, like in
#include <stdio.h>
#include <iostream>
char* toHexCharP(unsigned int hexValue, char *outBuffer, const size_t maxSizeOutBuffer)
{
snprintf(outBuffer,maxSizeOutBuffer-1,"0x%X",hexValue);
return outBuffer;
}
constexpr size_t MaxBufSize = 100U;
int main()
{
char buf[MaxBufSize];
std::cout << toHexCharP(15, buf, MaxBufSize) << '\n';
return 0;
}
But as said, I would not recomend. Too dangerous.
Your solution should look as follows:
std::string IntToHexString(int nDecimalNumber) {
std::ostringstream str;
str << std::hex << nDecimalNumber;
return str.str();
}
// ...
std::string transformed = IntToHexString(123);
You can then use transformed.c_str() to get your string as const char*.
Unless you have reasons to do so, you should never work with const char* in modern C++. Use std::string::c_str() if you need to.
So, I want to create a log file for an app I am trying to create and I don't know how to name the log to something like "log/date&time"
Anyway, here is my code:
#include <iostream>
#include <fstream>
#include <time.h>
#include <stdio.h>
#include <sstream>
using namespace std;
int main (int argc, char *argv[])
{
time_t t = time(0);
struct tm * now = localtime( & t );
char buffer [80];
strftime (buffer,80,"%Y-%m-%d.",now); //i think i can't just put "log/%Y-%m-%d." there.
ofstream myfile;
myfile.open ("log/" + buffer); // this is my problem, i can't put the ' "log/" + ' part there
if(myfile.is_open())
{
cout<<"Success"<<std::endl;
}
return 0;
}
You should use std::string which supports concatenation via the overloaded operator+.
std::string buffer(80, '\0');
strftime( &buffer[0], buffer.size(), "some format string", now);
/* ... */
std::ofstream myfile( ("log/" + buffer).c_str() );
// Remove the (..).c_str() part when working with a C++11 conforming
// standard library implementation
you actual question is "why doesnt this work"
myfile.open ("log/" + buffer);
answer - because c++ doesnt support what you want - concatenate a string literal with a char * and return another char *.
do
std::string filetime(buffer);
std::string filename = "log/" + filetime;
open(filename.c_str());
Consider using std:: facilities instead (std::string and std::ostringstream come to mind):
std::ostream& time_digits(std::ostream& out, unsigned int digits)
{ // convenience function: apply width and fill for the next input
return out << std::setw(digits) << std::setfill('0');
}
std::string unique_log_name()
{ // generate unique log name, depending on local time
// example output: "log/2014-04-19.log"
auto now = time(0);
tm *ltm = localtime(&now);
std::ostringstream buffer;
buffer
<< "log/" << time_digits(4) << ltm.tm_year
<< "-" << time_digits(2) << ltm.tm_mon
<< "-" << time_digits(2) << ltm.tm_day;
// could also add these to the name format:
// buffer
// << "-" << time_digits(2) << ltm.dm_hour
// << "-" << time_digits(2) << ltm.tm_min
// << "-" << time_digits(2) << ltm.tm_sec;
buffer << ".log"; // add extension
return buffer.str();
}
void client_code()
{ // construct log stream on unique file name
ofstream myfile{ unique_log_name() };
if(myfile)
{
cout << "Success" << std::endl;
}
}
How do I implement the following (Python pseudocode) in C++?
if argv[1].startswith('--foo='):
foo_value = int(argv[1][len('--foo='):])
(For example, if argv[1] is --foo=98, then foo_value is 98.)
Update: I'm hesitant to look into Boost, since I'm just looking at making a very small change to a simple little command-line tool (I'd rather not have to learn how to link in and use Boost for a minor change).
Use rfind overload that takes the search position pos parameter, and pass zero for it:
std::string s = "tititoto";
if (s.rfind("titi", 0) == 0) { // pos=0 limits the search to the prefix
// s starts with prefix
}
Who needs anything else? Pure STL!
Many have misread this to mean "search backwards through the whole string looking for the prefix". That would give the wrong result (e.g. string("tititito").rfind("titi") returns 2 so when compared against == 0 would return false) and it would be inefficient (looking through the whole string instead of just the start). But it does not do that because it passes the pos parameter as 0, which limits the search to only match at that position or earlier. For example:
std::string test = "0123123";
size_t match1 = test.rfind("123"); // returns 4 (rightmost match)
size_t match2 = test.rfind("123", 2); // returns 1 (skipped over later match)
size_t match3 = test.rfind("123", 0); // returns std::string::npos (i.e. not found)
You would do it like this:
std::string prefix("--foo=");
if (!arg.compare(0, prefix.size(), prefix))
foo_value = std::stoi(arg.substr(prefix.size()));
Looking for a lib such as Boost.ProgramOptions that does this for you is also a good idea.
Just for completeness, I will mention the C way to do it:
If str is your original string, substr is the substring you want to
check, then
strncmp(str, substr, strlen(substr))
will return 0 if str
starts with substr. The functions strncmp and strlen are in the C
header file <string.h>
(originally posted by Yaseen Rauf here, markup added)
For a case-insensitive comparison, use strnicmp instead of strncmp.
This is the C way to do it, for C++ strings you can use the same function like this:
strncmp(str.c_str(), substr.c_str(), substr.size())
If you're already using Boost, you can do it with boost string algorithms + boost lexical cast:
#include <boost/algorithm/string/predicate.hpp>
#include <boost/lexical_cast.hpp>
try {
if (boost::starts_with(argv[1], "--foo="))
foo_value = boost::lexical_cast<int>(argv[1]+6);
} catch (boost::bad_lexical_cast) {
// bad parameter
}
This kind of approach, like many of the other answers provided here is ok for very simple tasks, but in the long run you are usually better off using a command line parsing library. Boost has one (Boost.Program_options), which may make sense if you happen to be using Boost already.
Otherwise a search for "c++ command line parser" will yield a number of options.
Code I use myself:
std::string prefix = "-param=";
std::string argument = argv[1];
if(argument.substr(0, prefix.size()) == prefix) {
std::string argumentValue = argument.substr(prefix.size());
}
Nobody used the STL algorithm/mismatch function yet. If this returns true, prefix is a prefix of 'toCheck':
std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()
Full example prog:
#include <algorithm>
#include <string>
#include <iostream>
int main(int argc, char** argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "Will print true if 'prefix' is a prefix of string" << std::endl;
return -1;
}
std::string prefix(argv[1]);
std::string toCheck(argv[2]);
if (prefix.length() > toCheck.length()) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "'prefix' is longer than 'string'" << std::endl;
return 2;
}
if (std::mismatch(prefix.begin(), prefix.end(), toCheck.begin()).first == prefix.end()) {
std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck << '"' << std::endl;
return 0;
} else {
std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"' << toCheck << '"' << std::endl;
return 1;
}
}
Edit:
As #James T. Huggett suggests, std::equal is a better fit for the question: Is A a prefix of B? and is slight shorter code:
std::equal(prefix.begin(), prefix.end(), toCheck.begin())
Full example prog:
#include <algorithm>
#include <string>
#include <iostream>
int main(int argc, char **argv) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "Will print true if 'prefix' is a prefix of string"
<< std::endl;
return -1;
}
std::string prefix(argv[1]);
std::string toCheck(argv[2]);
if (prefix.length() > toCheck.length()) {
std::cerr << "Usage: " << argv[0] << " prefix string" << std::endl
<< "'prefix' is longer than 'string'" << std::endl;
return 2;
}
if (std::equal(prefix.begin(), prefix.end(), toCheck.begin())) {
std::cout << '"' << prefix << '"' << " is a prefix of " << '"' << toCheck
<< '"' << std::endl;
return 0;
} else {
std::cout << '"' << prefix << '"' << " is NOT a prefix of " << '"'
<< toCheck << '"' << std::endl;
return 1;
}
}
With C++17 you can use std::basic_string_view & with C++20 std::basic_string::starts_with or std::basic_string_view::starts_with.
The benefit of std::string_view in comparison to std::string - regarding memory management - is that it only holds a pointer to a "string" (contiguous sequence of char-like objects) and knows its size. Example without moving/copying the source strings just to get the integer value:
#include <exception>
#include <iostream>
#include <string>
#include <string_view>
int main()
{
constexpr auto argument = "--foo=42"; // Emulating command argument.
constexpr auto prefix = "--foo=";
auto inputValue = 0;
constexpr auto argumentView = std::string_view(argument);
if (argumentView.starts_with(prefix))
{
constexpr auto prefixSize = std::string_view(prefix).size();
try
{
// The underlying data of argumentView is nul-terminated, therefore we can use data().
inputValue = std::stoi(argumentView.substr(prefixSize).data());
}
catch (std::exception & e)
{
std::cerr << e.what();
}
}
std::cout << inputValue; // 42
}
Given that both strings — argv[1] and "--foo" — are C strings, #FelixDombek's answer is hands-down the best solution.
Seeing the other answers, however, I thought it worth noting that, if your text is already available as a std::string, then a simple, zero-copy, maximally efficient solution exists that hasn't been mentioned so far:
const char * foo = "--foo";
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(strlen(foo));
And if foo is already a string:
std::string foo("--foo");
if (text.rfind(foo, 0) == 0)
foo_value = text.substr(foo.length());
Starting with C++20, you can use the starts_with method.
std::string s = "abcd";
if (s.starts_with("abc")) {
...
}
text.substr(0, start.length()) == start
Using STL this could look like:
std::string prefix = "--foo=";
std::string arg = argv[1];
if (prefix.size()<=arg.size() && std::equal(prefix.begin(), prefix.end(), arg.begin())) {
std::istringstream iss(arg.substr(prefix.size()));
iss >> foo_value;
}
At the risk of being flamed for using C constructs, I do think this sscanf example is more elegant than most Boost solutions. And you don't have to worry about linkage if you're running anywhere that has a Python interpreter!
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
for (int i = 1; i != argc; ++i) {
int number = 0;
int size = 0;
sscanf(argv[i], "--foo=%d%n", &number, &size);
if (size == strlen(argv[i])) {
printf("number: %d\n", number);
}
else {
printf("not-a-number\n");
}
}
return 0;
}
Here's some example output that demonstrates the solution handles leading/trailing garbage as correctly as the equivalent Python code, and more correctly than anything using atoi (which will erroneously ignore a non-numeric suffix).
$ ./scan --foo=2 --foo=2d --foo='2 ' ' --foo=2'
number: 2
not-a-number
not-a-number
not-a-number
I use std::string::compare wrapped in utility method like below:
static bool startsWith(const string& s, const string& prefix) {
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
}
C++20 update :
Use std::string::starts_with
https://en.cppreference.com/w/cpp/string/basic_string/starts_with
std::string str_value = /* smthg */;
const auto starts_with_foo = str_value.starts_with(std::string_view{"foo"});
In C++20 now there is starts_with available as a member function of std::string defined as:
constexpr bool starts_with(string_view sv) const noexcept;
constexpr bool starts_with(CharT c) const noexcept;
constexpr bool starts_with(const CharT* s) const;
So your code could be something like this:
std::string s{argv[1]};
if (s.starts_with("--foo="))
In case you need C++11 compatibility and cannot use boost, here is a boost-compatible drop-in with an example of usage:
#include <iostream>
#include <string>
static bool starts_with(const std::string str, const std::string prefix)
{
return ((prefix.size() <= str.size()) && std::equal(prefix.begin(), prefix.end(), str.begin()));
}
int main(int argc, char* argv[])
{
bool usage = false;
unsigned int foos = 0; // default number of foos if no parameter was supplied
if (argc > 1)
{
const std::string fParamPrefix = "-f="; // shorthand for foo
const std::string fooParamPrefix = "--foo=";
for (unsigned int i = 1; i < argc; ++i)
{
const std::string arg = argv[i];
try
{
if ((arg == "-h") || (arg == "--help"))
{
usage = true;
} else if (starts_with(arg, fParamPrefix)) {
foos = std::stoul(arg.substr(fParamPrefix.size()));
} else if (starts_with(arg, fooParamPrefix)) {
foos = std::stoul(arg.substr(fooParamPrefix.size()));
}
} catch (std::exception& e) {
std::cerr << "Invalid parameter: " << argv[i] << std::endl << std::endl;
usage = true;
}
}
}
if (usage)
{
std::cerr << "Usage: " << argv[0] << " [OPTION]..." << std::endl;
std::cerr << "Example program for parameter parsing." << std::endl << std::endl;
std::cerr << " -f, --foo=N use N foos (optional)" << std::endl;
return 1;
}
std::cerr << "number of foos given: " << foos << std::endl;
}
Why not use gnu getopts? Here's a basic example (without safety checks):
#include <getopt.h>
#include <stdio.h>
int main(int argc, char** argv)
{
option long_options[] = {
{"foo", required_argument, 0, 0},
{0,0,0,0}
};
getopt_long(argc, argv, "f:", long_options, 0);
printf("%s\n", optarg);
}
For the following command:
$ ./a.out --foo=33
You will get
33
Ok why the complicated use of libraries and stuff? C++ String objects overload the [] operator, so you can just compare chars.. Like what I just did, because I want to list all files in a directory and ignore invisible files and the .. and . pseudofiles.
while ((ep = readdir(dp)))
{
string s(ep->d_name);
if (!(s[0] == '.')) // Omit invisible files and .. or .
files.push_back(s);
}
It's that simple..
You can also use strstr:
if (strstr(str, substr) == substr) {
// 'str' starts with 'substr'
}
but I think it's good only for short strings because it has to loop through the whole string when the string doesn't actually start with 'substr'.
With C++11 or higher you can use find() and find_first_of()
Example using find to find a single char:
#include <string>
std::string name = "Aaah";
size_t found_index = name.find('a');
if (found_index != std::string::npos) {
// Found string containing 'a'
}
Example using find to find a full string & starting from position 5:
std::string name = "Aaah";
size_t found_index = name.find('h', 3);
if (found_index != std::string::npos) {
// Found string containing 'h'
}
Example using the find_first_of() and only the first char, to search at the start only:
std::string name = ".hidden._di.r";
size_t found_index = name.find_first_of('.');
if (found_index == 0) {
// Found '.' at first position in string
}
More about find
More about find_first_of
Good luck!
std::string text = "--foo=98";
std::string start = "--foo=";
if (text.find(start) == 0)
{
int n = stoi(text.substr(start.length()));
std::cout << n << std::endl;
}
Since C++11 std::regex_search can also be used to provide even more complex expressions matching. The following example handles also floating numbers thorugh std::stof and a subsequent cast to int.
However the parseInt method shown below could throw a std::invalid_argument exception if the prefix is not matched; this can be easily adapted depending on the given application:
#include <iostream>
#include <regex>
int parseInt(const std::string &str, const std::string &prefix) {
std::smatch match;
std::regex_search(str, match, std::regex("^" + prefix + "([+-]?(?=\\.?\\d)\\d*(?:\\.\\d*)?(?:[Ee][+-]?\\d+)?)$"));
return std::stof(match[1]);
}
int main() {
std::cout << parseInt("foo=13.3", "foo=") << std::endl;
std::cout << parseInt("foo=-.9", "foo=") << std::endl;
std::cout << parseInt("foo=+13.3", "foo=") << std::endl;
std::cout << parseInt("foo=-0.133", "foo=") << std::endl;
std::cout << parseInt("foo=+00123456", "foo=") << std::endl;
std::cout << parseInt("foo=-06.12e+3", "foo=") << std::endl;
// throw std::invalid_argument
// std::cout << parseInt("foo=1", "bar=") << std::endl;
return 0;
}
The kind of magic of the regex pattern is well detailed in the following answer.
EDIT: the previous answer did not performed the conversion to integer.
if(boost::starts_with(string_to_search, string_to_look_for))
intval = boost::lexical_cast<int>(string_to_search.substr(string_to_look_for.length()));
This is completely untested. The principle is the same as the Python one. Requires Boost.StringAlgo and Boost.LexicalCast.
Check if the string starts with the other string, and then get the substring ('slice') of the first string and convert it using lexical cast.
C# has a syntax feature where you can concatenate many data types together on 1 line.
string s = new String();
s += "Hello world, " + myInt + niceToSeeYouString;
s += someChar1 + interestingDecimal + someChar2;
What would be the equivalent in C++? As far as I can see, you'd have to do it all on separate lines as it doesn't support multiple strings/variables with the + operator. This is OK, but doesn't look as neat.
string s;
s += "Hello world, " + "nice to see you, " + "or not.";
The above code produces an error.
#include <sstream>
#include <string>
std::stringstream ss;
ss << "Hello, world, " << myInt << niceToSeeYouString;
std::string s = ss.str();
Take a look at this Guru Of The Week article from Herb Sutter: The String Formatters of Manor Farm
In 5 years nobody has mentioned .append?
#include <string>
std::string s;
s.append("Hello world, ");
s.append("nice to see you, ");
s.append("or not.");
s += "Hello world, " + "nice to see you, " + "or not.";
Those character array literals are not C++ std::strings - you need to convert them:
s += string("Hello world, ") + string("nice to see you, ") + string("or not.");
To convert ints (or any other streamable type) you can use a boost lexical_cast or provide your own function:
template <typename T>
string Str( const T & t ) {
ostringstream os;
os << t;
return os.str();
}
You can now say things like:
string s = string("The meaning is ") + Str( 42 );
Your code can be written as1,
s = "Hello world," "nice to see you," "or not."
...but I doubt that's what you're looking for. In your case, you are probably looking for streams:
std::stringstream ss;
ss << "Hello world, " << 42 << "nice to see you.";
std::string s = ss.str();
1 "can be written as" : This only works for string literals. The concatenation is done by the compiler.
Using C++14 user defined literals and std::to_string the code becomes easier.
using namespace std::literals::string_literals;
std::string str;
str += "Hello World, "s + "nice to see you, "s + "or not"s;
str += "Hello World, "s + std::to_string(my_int) + other_string;
Note that concatenating string literals can be done at compile time. Just remove the +.
str += "Hello World, " "nice to see you, " "or not";
In C++20 you'll be able to do:
auto s = std::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);
Until then you could do the same with the {fmt} library:
auto s = fmt::format("{}{}{}", "Hello world, ", myInt, niceToSeeYouString);
Disclaimer: I'm the author of {fmt}.
To offer a solution that is more one-line-ish: A function concat can be implemented to reduce the "classic" stringstream based solution to a single statement.
It is based on variadic templates and perfect forwarding.
Usage:
std::string s = concat(someObject, " Hello, ", 42, " I concatenate", anyStreamableType);
Implementation:
void addToStream(std::ostringstream&)
{
}
template<typename T, typename... Args>
void addToStream(std::ostringstream& a_stream, T&& a_value, Args&&... a_args)
{
a_stream << std::forward<T>(a_value);
addToStream(a_stream, std::forward<Args>(a_args)...);
}
template<typename... Args>
std::string concat(Args&&... a_args)
{
std::ostringstream s;
addToStream(s, std::forward<Args>(a_args)...);
return s.str();
}
boost::format
or std::stringstream
std::stringstream msg;
msg << "Hello world, " << myInt << niceToSeeYouString;
msg.str(); // returns std::string object
auto s = string("one").append("two").append("three")
The actual problem was that concatenating string literals with + fails in C++:
string s;
s += "Hello world, " + "nice to see you, " + "or not.";
The above code produces an error.
In C++ (also in C), you concatenate string literals by just placing them right next to each other:
string s0 = "Hello world, " "nice to see you, " "or not.";
string s1 = "Hello world, " /*same*/ "nice to see you, " /*result*/ "or not.";
string s2 =
"Hello world, " /*line breaks in source code as well as*/
"nice to see you, " /*comments don't matter*/
"or not.";
This makes sense, if you generate code in macros:
#define TRACE(arg) cout << #arg ":" << (arg) << endl;
...a simple macro that can be used like this
int a = 5;
TRACE(a)
a += 7;
TRACE(a)
TRACE(a+7)
TRACE(17*11)
(live demo ...)
or, if you insist in using the + for string literals (as already suggested by underscore_d):
string s = string("Hello world, ")+"nice to see you, "+"or not.";
Another solution combines a string and a const char* for each concatenation step
string s;
s += "Hello world, "
s += "nice to see you, "
s += "or not.";
You would have to define operator+() for every data type you would want to concenate to the string, yet since operator<< is defined for most types, you should use std::stringstream.
Damn, beat by 50 seconds...
If you write out the +=, it looks almost the same as C#
string s("Some initial data. "); int i = 5;
s = s + "Hello world, " + "nice to see you, " + to_string(i) + "\n";
As others said, the main problem with the OP code is that the operator + does not concatenate const char *; it works with std::string, though.
Here's another solution that uses C++11 lambdas and for_each and allows to provide a separator to separate the strings:
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
string join(const string& separator,
const vector<string>& strings)
{
if (strings.empty())
return "";
if (strings.size() == 1)
return strings[0];
stringstream ss;
ss << strings[0];
auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
for_each(begin(strings) + 1, end(strings), aggregate);
return ss.str();
}
Usage:
std::vector<std::string> strings { "a", "b", "c" };
std::string joinedStrings = join(", ", strings);
It seems to scale well (linearly), at least after a quick test on my computer; here's a quick test I've written:
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <chrono>
using namespace std;
string join(const string& separator,
const vector<string>& strings)
{
if (strings.empty())
return "";
if (strings.size() == 1)
return strings[0];
stringstream ss;
ss << strings[0];
auto aggregate = [&ss, &separator](const string& s) { ss << separator << s; };
for_each(begin(strings) + 1, end(strings), aggregate);
return ss.str();
}
int main()
{
const int reps = 1000;
const string sep = ", ";
auto generator = [](){return "abcde";};
vector<string> strings10(10);
generate(begin(strings10), end(strings10), generator);
vector<string> strings100(100);
generate(begin(strings100), end(strings100), generator);
vector<string> strings1000(1000);
generate(begin(strings1000), end(strings1000), generator);
vector<string> strings10000(10000);
generate(begin(strings10000), end(strings10000), generator);
auto t1 = chrono::system_clock::now();
for(int i = 0; i<reps; ++i)
{
join(sep, strings10);
}
auto t2 = chrono::system_clock::now();
for(int i = 0; i<reps; ++i)
{
join(sep, strings100);
}
auto t3 = chrono::system_clock::now();
for(int i = 0; i<reps; ++i)
{
join(sep, strings1000);
}
auto t4 = chrono::system_clock::now();
for(int i = 0; i<reps; ++i)
{
join(sep, strings10000);
}
auto t5 = chrono::system_clock::now();
auto d1 = chrono::duration_cast<chrono::milliseconds>(t2 - t1);
auto d2 = chrono::duration_cast<chrono::milliseconds>(t3 - t2);
auto d3 = chrono::duration_cast<chrono::milliseconds>(t4 - t3);
auto d4 = chrono::duration_cast<chrono::milliseconds>(t5 - t4);
cout << "join(10) : " << d1.count() << endl;
cout << "join(100) : " << d2.count() << endl;
cout << "join(1000) : " << d3.count() << endl;
cout << "join(10000): " << d4.count() << endl;
}
Results (milliseconds):
join(10) : 2
join(100) : 10
join(1000) : 91
join(10000): 898
Here's the one-liner solution:
#include <iostream>
#include <string>
int main() {
std::string s = std::string("Hi") + " there" + " friends";
std::cout << s << std::endl;
std::string r = std::string("Magic number: ") + std::to_string(13) + "!";
std::cout << r << std::endl;
return 0;
}
Although it's a tiny bit ugly, I think it's about as clean as you cat get in C++.
We are casting the first argument to a std::string and then using the (left to right) evaluation order of operator+ to ensure that its left operand is always a std::string. In this manner, we concatenate the std::string on the left with the const char * operand on the right and return another std::string, cascading the effect.
Note: there are a few options for the right operand, including const char *, std::string, and char.
It's up to you to decide whether the magic number is 13 or 6227020800.
Maybe you like my "Streamer" solution to really do it in one line:
#include <iostream>
#include <sstream>
using namespace std;
class Streamer // class for one line string generation
{
public:
Streamer& clear() // clear content
{
ss.str(""); // set to empty string
ss.clear(); // clear error flags
return *this;
}
template <typename T>
friend Streamer& operator<<(Streamer& streamer,T str); // add to streamer
string str() // get current string
{ return ss.str();}
private:
stringstream ss;
};
template <typename T>
Streamer& operator<<(Streamer& streamer,T str)
{ streamer.ss<<str;return streamer;}
Streamer streamer; // make this a global variable
class MyTestClass // just a test class
{
public:
MyTestClass() : data(0.12345){}
friend ostream& operator<<(ostream& os,const MyTestClass& myClass);
private:
double data;
};
ostream& operator<<(ostream& os,const MyTestClass& myClass) // print test class
{ return os<<myClass.data;}
int main()
{
int i=0;
string s1=(streamer.clear()<<"foo"<<"bar"<<"test").str(); // test strings
string s2=(streamer.clear()<<"i:"<<i++<<" "<<i++<<" "<<i++<<" "<<0.666).str(); // test numbers
string s3=(streamer.clear()<<"test class:"<<MyTestClass()).str(); // test with test class
cout<<"s1: '"<<s1<<"'"<<endl;
cout<<"s2: '"<<s2<<"'"<<endl;
cout<<"s3: '"<<s3<<"'"<<endl;
}
You may use this header for this regard: https://github.com/theypsilon/concat
using namespace concat;
assert(concat(1,2,3,4,5) == "12345");
Under the hood you will be using a std::ostringstream.
If you are willing to use c++11 you can utilize user-defined string literals and define two function templates that overload the plus operator for a std::string object and any other object. The only pitfall is not to overload the plus operators of std::string, otherwise the compiler doesn't know which operator to use. You can do this by using the template std::enable_if from type_traits. After that strings behave just like in Java or C#. See my example implementation for details.
Main code
#include <iostream>
#include "c_sharp_strings.hpp"
using namespace std;
int main()
{
int i = 0;
float f = 0.4;
double d = 1.3e-2;
string s;
s += "Hello world, "_ + "nice to see you. "_ + i
+ " "_ + 47 + " "_ + f + ',' + d;
cout << s << endl;
return 0;
}
File c_sharp_strings.hpp
Include this header file in all all places where you want to have these strings.
#ifndef C_SHARP_STRING_H_INCLUDED
#define C_SHARP_STRING_H_INCLUDED
#include <type_traits>
#include <string>
inline std::string operator "" _(const char a[], long unsigned int i)
{
return std::string(a);
}
template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
!std::is_same<char, T>::value &&
!std::is_same<const char*, T>::value, std::string>::type
operator+ (std::string s, T i)
{
return s + std::to_string(i);
}
template<typename T> inline
typename std::enable_if<!std::is_same<std::string, T>::value &&
!std::is_same<char, T>::value &&
!std::is_same<const char*, T>::value, std::string>::type
operator+ (T i, std::string s)
{
return std::to_string(i) + s;
}
#endif // C_SHARP_STRING_H_INCLUDED
Something like this works for me
namespace detail {
void concat_impl(std::ostream&) { /* do nothing */ }
template<typename T, typename ...Args>
void concat_impl(std::ostream& os, const T& t, Args&&... args)
{
os << t;
concat_impl(os, std::forward<Args>(args)...);
}
} /* namespace detail */
template<typename ...Args>
std::string concat(Args&&... args)
{
std::ostringstream os;
detail::concat_impl(os, std::forward<Args>(args)...);
return os.str();
}
// ...
std::string s{"Hello World, "};
s = concat(s, myInt, niceToSeeYouString, myChar, myFoo);
Based on above solutions I made a class var_string for my project to make life easy. Examples:
var_string x("abc %d %s", 123, "def");
std::string y = (std::string)x;
const char *z = x.c_str();
The class itself:
#include <stdlib.h>
#include <stdarg.h>
class var_string
{
public:
var_string(const char *cmd, ...)
{
va_list args;
va_start(args, cmd);
vsnprintf(buffer, sizeof(buffer) - 1, cmd, args);
}
~var_string() {}
operator std::string()
{
return std::string(buffer);
}
operator char*()
{
return buffer;
}
const char *c_str()
{
return buffer;
}
int system()
{
return ::system(buffer);
}
private:
char buffer[4096];
};
Still wondering if there will be something better in C++ ?
In c11:
void printMessage(std::string&& message) {
std::cout << message << std::endl;
return message;
}
this allow you to create function call like this:
printMessage("message number : " + std::to_string(id));
will print : message number : 10
you can also "extend" the string class and choose the operator you prefer ( <<, &, |, etc ...)
Here is the code using operator<< to show there is no conflict with streams
note: if you uncomment s1.reserve(30), there is only 3 new() operator requests (1 for s1, 1 for s2, 1 for reserve ; you can't reserve at constructor time unfortunately); without reserve, s1 has to request more memory as it grows, so it depends on your compiler implementation grow factor (mine seems to be 1.5, 5 new() calls in this example)
namespace perso {
class string:public std::string {
public:
string(): std::string(){}
template<typename T>
string(const T v): std::string(v) {}
template<typename T>
string& operator<<(const T s){
*this+=s;
return *this;
}
};
}
using namespace std;
int main()
{
using string = perso::string;
string s1, s2="she";
//s1.reserve(30);
s1 << "no " << "sunshine when " << s2 << '\'' << 's' << " gone";
cout << "Aint't "<< s1 << " ..." << endl;
return 0;
}
Stringstream with a simple preproccessor macro using a lambda function seems nice:
#include <sstream>
#define make_string(args) []{std::stringstream ss; ss << args; return ss;}()
and then
auto str = make_string("hello" << " there" << 10 << '$');
This works for me:
#include <iostream>
using namespace std;
#define CONCAT2(a,b) string(a)+string(b)
#define CONCAT3(a,b,c) string(a)+string(b)+string(c)
#define CONCAT4(a,b,c,d) string(a)+string(b)+string(c)+string(d)
#define HOMEDIR "c:\\example"
int main()
{
const char* filename = "myfile";
string path = CONCAT4(HOMEDIR,"\\",filename,".txt");
cout << path;
return 0;
}
Output:
c:\example\myfile.txt
Have you tried to avoid the +=?
instead use var = var + ...
it worked for me.
#include <iostream.h> // for string
string myName = "";
int _age = 30;
myName = myName + "Vincent" + "Thorpe" + 30 + " " + 2019;