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());
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.
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;