Write a Macro to stringify the contents of a struct - c++

I am trying to write a macro to expand the contents of a struct:
struct Str
{
int a;
float f;
char *c;
};
Str s = {123, 456.789f, "AString"};
#define STRINGIFY_STR(x) ... // Macro to stringify x which is an instance of Str
printf("%s", STRINGIFY_STR(s));
desired output: [a: 123, f:456.789, c:AString]
Is it possible to write a macro that does this? If it is, then how?

Is there a reason you want to do this as a macro?
You should write a function to perform this action instead of using the preprocessor.
Depending on your goals, there are a few options. The boost formatting library provides a great toolkit to build the formatted string. You could always overload operator<< to provide clean output, as well.
If you're doing this in pure C, the sprintf family of methods work for creating formatted output.

This is quite gross, and only works on gcc/g++.
#define STRINGIFY_STR(x) \
std::string(({ std::ostringstream ss; \
ss << "[a: " << x.a \
<< ", f: " << x.f \
<< ", c: " << x.c << "]"; \
ss.str(); })).c_str()
You have to create the string from the values. Please don't do it this way. Please follow Reed's advice.
Here is how I would modify your struct to allow it to be pretty printed:
struct Str
{
int a;
float f;
char *c;
std::ostream & dump (std::ostream &os) const {
return os << "[a: " << a
<< ", f: " << f
<< ", c: " << c << "]";
}
};
std::ostream & operator << (std::ostream &os, const Str &s) {
return s.dump(os);
}
Str s = {123, 456.789f, "AString"};
Now, to print out s, you can use std::cout:
std::cout << s << std::endl;
Or, if you really want a string:
std::stringstream ss;
s.dump(ss);
puts(ss.str().c_str());

struct stringify
{
operator std::string() const
{
return str ;
}
std::string str ;
};
template < typename T > stringify operator+ ( stringify s, const T& object )
{
std::ostringstream stm ;
stm << object ;
if( !s.str.empty() && !std::isspace( *s.str.rbegin() ) ) s.str += ' ' ;
s.str += stm.str() ;
return s ;
}

I'm also against the approach, but to answer the question:
#define STRINGIFY_STR(x) \
(std::string("[a: ") + std::to_string((long double)x.a) \
+ std::string(", f:") + std::to_string((long double)x.f) \
+ std::string(",c: ") + x.c + std::string("]") ).c_str()
I suggest writing a member function that does this though.

Related

c++ concatenate different types into a string for function

I have different parts of my application calling a logger function to log details.
Logger class
std::string filename = "blahblah"; // variable to store the location of the properties file
log4cpp::PropertyConfigurator::configure(filename);
void Logger::logging(const std::string& msg)
{
Log4cpp::Category& myLogger = log4cpp::Category::getRoot();
myLogger.log(log4cpp::Priority::INFO, msg);//only takes in string as input
}
Calling class
Logger logMe;
int a = 5;
double b = 6;
logMe.logging("log this msg" + a + "," + b);
I realised that the above will give me error, as a and b are of different types. One way to solve it is to use std::to_string
logMe.logging("log this msg" + std::to_string(a) + "," + std::to_string(b));
However, I have hundreds of calls to the logging function, and it will be time consuming to edit each and every call to std::to_string. Is/are there any easier way(s) to do it?
Oh and to clarify, the code works before as the previous way was by defining a #define function.
#Define logging(FLAG, X)\
do {\
...
clog << x; \
}while(0)
logging(LogFlag::Warning, "log this msg" << a << "," << b << endl);
But i am now rewriting parts of the code to comply to static testing.
Thanks in advance.
You can add an overload of logging that takes a parameter pack and joins it up into a string, using std::stringstream
In c++17, we can use a fold expression, e.g.
template <typename Args ...>
void Logger::logging(Args ... args)
{
std::stringstream ss;
(ss << ... << args);
Log4cpp::Category& myLogger = log4cpp::Category::getRoot();
myLogger.log(log4cpp::Priority::INFO, ss.str());
}
In c++11 or 14, we have to be slightly more tricky
template <typename ... Args >
void Logger::logging(Args ... args)
{
std::stringstream ss;
std::initializer_list<int> unused{ (ss << args, 0)... };
Log4cpp::Category& myLogger = log4cpp::Category::getRoot();
myLogger.log(log4cpp::Priority::INFO, ss.str());
}
Then you call either as e.g.
logMe.logging("log this msg", a, ",", b);
I suggest adding an operator<<() to the class
class Logger
{
public:
Logger &operator<<(const std::string &s)
{
logging(s)
return *this;
};
Logger &operator<<(const char *s)
{
return operator<<(std::string(s));
}
template <class T>
Logger &operator<<(const T &v)
{
std::ostringstream s;
s << v;
return operator<<(logging(ss.str()));
};
// other stuff you have in your class, including the logging() function
};
// to use
logMe << "log this msg" << a << b;
Not quite the same syntax to use this as you have described, but it works more generally.
It's fairly easy to use a stringstream. You can then convert it to std::string using str().
#include <sstream>
...
int a = 5;
double b = 6;
std::stringstream ss;
ss << "log this msg" << a << b;
std::cout << ss.str() << std::endl;
logMe.logging(ss.str());

Quoting strings in C++

In Pascal Lazarus/Delphi, we have a function QuotedStr() that wraps any string within single quotes.
Here's an example of my current C++ code:
//I need to quote tblCustomers
pqxx::result r = txn.exec( "Select * from \"tblCustomers\" ");
Another one:
//I need to quote cCustomerName
std::cout << "Name: " << r[a]["\"cCustomerName\""];
Similar to the above, I have to frequently double-quote strings. Typing this in is kind of slowing me down. Is there a standard function I can use for this?
BTW, I develop using Ubuntu/Windows with Code::Blocks. The technique used must be compatible across both platforms. If there's no function, this means that I must write one.
C++14 added std::quoted which does exactly that, and more actually: it takes care of escaping quotes and backslashes in output streams, and of unescaping them in input streams. It is efficient, in that it does not create a new string, it's really a IO manipulator. (So you don't get a string, as you'd like.)
#include <iostream>
#include <iomanip>
#include <sstream>
int main()
{
std::string in = "\\Hello \"Wörld\"\\\n";
std::stringstream ss;
ss << std::quoted(in);
std::string out;
ss >> std::quoted(out);
std::cout << '{' << in << "}\n"
<< '{' << ss.str() << "}\n"
<< '{' << out << "}\n";
}
gives
{\Hello "Wörld"\
}
{"\\Hello \"Wörld\"\\
"}
{\Hello "Wörld"\
}
As described in its proposal, it was really designed for round-tripping of strings.
Using C++11 you can create user defined literals like this:
#include <iostream>
#include <string>
#include <cstddef>
// Define user defined literal "_quoted" operator.
std::string operator"" _quoted(const char* text, std::size_t len) {
return "\"" + std::string(text, len) + "\"";
}
int main() {
std::cout << "tblCustomers"_quoted << std::endl;
std::cout << "cCustomerName"_quoted << std::endl;
}
Output:
"tblCustomers"
"cCustomerName"
You can even define the operator with a shorter name if you want, e.g.:
std::string operator"" _q(const char* text, std::size_t len) { /* ... */ }
// ...
std::cout << "tblCustomers"_q << std::endl;
More info on user-defined literals
String str = "tblCustomers";
str = "'" + str + "'";
See more options here
No standard function, unless you count std::basic_string::operator+(), but writing it is trivial.
I'm somewhat confused by what's slowing you down - quoted( "cCustomerName" ) is more characters, no? :>
You could use your own placeholder character to stand for the quote, some ASCII symbol that will never be used, and replace it with " just before you output the strings.
#include <iostream>
#include <string>
struct quoted
{
const char * _text;
quoted( const char * text ) : _text(text) {}
operator std::string () const
{
std::string quotedStr = "\"";
quotedStr += _text;
quotedStr += "\"";
return quotedStr;
}
};
std::ostream & operator<< ( std::ostream & ostr, const quoted & q )
{
ostr << "\"" << q._text << "\"";
return ostr;
}
int main ( int argc, char * argv[] )
{
std::string strq = quoted( "tblCustomers" );
std::cout << strq << std::endl;
std::cout << quoted( "cCustomerName" ) << std::endl;
return 0;
}
With this you get what you want.
What about using some C function and backslash to escape the quotes?
Like sprintf_s:
#define BUF_SIZE 100
void execute_prog() {
//Strings that will be predicted by quotes
string param1 = "C:\\users\\foo\\input file.txt", string param2 = "output.txt";
//Char array with any buffer size
char command[BUF_SIZE];
//Concating my prog call in the C string.
//sprintf_s requires a buffer size for security reasons
sprintf_s(command, BUF_SIZE, "program.exe \"%s\" \"%s\"", param1.c_str(),
param2.c_str());
system(command);
}
Resulting string is:
program.exe "C:\users\foo\input file.txt" "output.txt"
Here is the documentation.

sprintf in c++?

I'm searching for an sprintf in c++.
I want to build a mysql query string but if I do it like (max_limit is an const int)
std::string query = "select * from bla limit " + max_limit;
The query wont work.
In C++11, this has been made too easy. Use std::to_string() as:
std::string query = "select * from bla limit " + std::to_string(max_limit);
Done!
OLD SOLUTION, for those who still use C++03.
Use stringbuilder and create std::string on the fly as:
std::string query = stringbuilder() << "select * from bla limit " << max_limit;
where stringbuilder is implemented as:
struct stringbuilder
{
std::stringstream ss;
template<typename T>
stringbuilder & operator << (const T &data)
{
ss << data;
return *this;
}
operator std::string() { return ss.str(); }
};
You can use stringbuilder in many different ways, such as:
std::string g(int m, int n)
{
//create string on the fly and returns it
if ( m < n )
return stringbuilder() << m << " is less than " << n ;
return stringbuilder() << n << " is less than " << m ;
}
void f(const std::string & s );
//call f while creating string on the fly and passing it to the function
f(stringbuilder() << '{' << pc << '}' ); //passed as std::string
//this is my most favorite line
std::string s = stringbuilder() << 23 << " is greater than " << 5 ;
See demo at ideone : http://ideone.com/J995r
And see my blog on this : Create string on the fly just in one line
You don't want sprintf, it doesn't work with strings. Something like this:
#include <sstream>
#include <string>
template <typename T>
std::string Str( const T & t ) {
std::ostringstream os;
os << t;
return os.str();
}
will do the job. You can then say:
std::string query = "select * from bla limit " + Str( max_limit );
Maybe you want to take a look at the boost::format lib.
It provides the syntax of sprintf with the convenience of c++.
So your example would be:
std::string str = (boost::format("select * from bla limit %d") % max_limit).str();
Or just use a macro?
#define QueryString(msg) ((static_cast<std::ostringstream&>(std::ostringstream().seekp(0, std::ios_base::cur)<<msg)).str())
Usage:
std::string query = QueryString("select * from mytable where x="<<30);

Odd behavior with ostringstream

I was trying to think of a clever way to concatenate various things into a single string argument for a function without having to use an ostringstream explicitly. I thought of:
#define OSS(...) \
dynamic_cast<std::ostringstream const&>(std::ostringstream() << __VA_ARGS__).str()
However, given:
void f( string const &s ) {
cout << s << endl;
}
int main() {
char const *const s = "hello";
f( OSS( '{' << s << '}' ) );
ostringstream oss;
oss << '{' << s << '}';
cout << oss.str() << endl;
}
it prints when run:
123hello}
{hello}
where 123 is the ASCII code for }. Why does using the macro get it wrong?
FYI: I'm currently using g++ 4.2.1 on Mac OS X as part of Xcode 3.x.
Solution I'm now using
class string_builder {
public:
template<typename T>
string_builder& operator,( T const &t ) {
oss_ << t;
return *this;
}
operator std::string() const {
return oss_.str();
}
private:
std::ostringstream oss_;
};
#define BUILD_STRING(...) (string_builder(), __VA_ARGS__)
using namespace std;
void f( string const &s ) {
cout << s << endl;
}
int main() {
char const *const s = "hello";
f( BUILD_STRING( '{', s, '}' ) );
}
std::ostringstream() is temporary which thus can be bound only to const references. Standalone operator<< (which take non const references as first argument) aren't considered and only the member one are. The best match in these for a char is converting the char to int.
This problems occurs often with string literals whose address is then displayed.
To solve the problem, the trick is to find a way to transform the temporary in a reference. The member operator<<s do that, but only the one for manipulator does it without side effect and only if the manipulator is a noop -- flush could be used. The members flush and write are also candidates. So for instance
#define OSS(...) \
dynamic_cast<std::ostringstream const&>(std::ostringstream().flush() << __VA_ARGS__).str()
A better thread-safe solution without involving the cumbersome macro.
The original call to function is this:
f( OSS( '{' << s << '}' ) );
How about if the call is just this:
f(stringbuilder() << '{' << s << '}' );
where stringbuilder is implemented as:
struct stringbuilder
{
std::ostringstream ss;
template<typename T>
stringbuilder & operator << (const T &data)
{
ss << data;
return *this;
}
operator string() { return ss.str(); }
};
void f( string const &s ) {
cout << s << endl;
}
Test:
int main() {
char const *const s = "hello";
f(stringbuilder() << '{' << s << '}' );
}
Output:
{hello}
Online Demo : http://ideone.com/QHFf4

How do I concatenate multiple C++ strings on one line?

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;