We need to format strings all the time. It would be so nice to be able to say:
std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
Is there a C++ way of doing this? Some alternatives I considered:
snprintf: uses raw char buffers. Not nice in modern C++ code.
std::stringstream: does not support format pattern strings, instead you must push clumsy iomanip objects into the stream.
boost::format: uses an ad-hoc operator overload of % to specify the arguments. Ugly.
Isn't there a better way with variadic templates now that we have C++11?
It can certainly be written in C++11 with variadic templates. It's best to wrap something that already exists than to try to write the whole thing yourself. If you are already using Boost, it's quite simple to wrap boost::format like this:
#include <boost/format.hpp>
#include <string>
namespace details
{
boost::format& formatImpl(boost::format& f)
{
return f;
}
template <typename Head, typename... Tail>
boost::format& formatImpl(
boost::format& f,
Head const& head,
Tail&&... tail)
{
return formatImpl(f % head, std::forward<Tail>(tail)...);
}
}
template <typename... Args>
std::string format(
std::string formatString,
Args&&... args)
{
boost::format f(std::move(formatString));
return details::formatImpl(f, std::forward<Args>(args)...).str();
}
You can use this the way you wanted:
std::string formattedStr = format("%s_%06d.dat", "myfile", 18); // myfile_000018.dat
If you don't want to use Boost (but you really should) then you can also wrap snprintf. It is a bit more involved and error-prone, since we need to manage char buffers and the old style non-type-safe variable length argument list. It gets a bit cleaner by using unique_ptr's:
#include <cstdio> // snprintf
#include <string>
#include <stdexcept> // runtime_error
#include <memory> // unique_ptr
namespace details
{
template <typename... Args>
std::unique_ptr<char[]> formatImplS(
size_t bufSizeGuess,
char const* formatCStr,
Args&&... args)
{
std::unique_ptr<char[]> buf(new char[bufSizeGuess]);
size_t expandedStrLen = std::snprintf(buf.get(), bufSizeGuess, formatCStr, args...);
if (expandedStrLen >= 0 && expandedStrLen < bufSizeGuess)
{
return buf;
} else if (expandedStrLen >= 0
&& expandedStrLen < std::numeric_limits<size_t>::max())
{
// buffer was too small, redo with the correct size
return formatImplS(expandedStrLen+1, formatCStr, std::forward<Args>(args)...);
} else {
throw std::runtime_error("snprintf failed with return value: "+std::to_string(expandedStrLen));
}
}
char const* ifStringThenConvertToCharBuf(std::string const& cpp)
{
return cpp.c_str();
}
template <typename T>
T ifStringThenConvertToCharBuf(T const& t)
{
return t;
}
}
template <typename... Args>
std::string formatS(std::string const& formatString, Args&&... args)
{
// unique_ptr<char[]> calls delete[] on destruction
std::unique_ptr<char[]> chars = details::formatImplS(4096, formatString.c_str(),
details::ifStringThenConvertToCharBuf(args)...);
// string constructor copies the data
return std::string(chars.get());
}
There are some differences between snprintf and boost::format in terms of format specification but your example works with both.
The fmt library implements exactly that, string formatting using variadic templates. Example:
// printf syntax:
std::string formattedStr = fmt::sprintf("%s_%06d.dat", "myfile", 18);
// Python-like syntax:
std::string formattedStr = fmt::format("{}_{:06}.dat", "myfile", 18);
Disclaimer: I'm the author of the library.
Related
I am trying to write a function that can convert its argument into a string. However, I am finding it difficult to unpack the parameter pack.
Here is the code that I have written:
#include <iostream>
#include <sstream>
template <typename... T>
std::string StringFormatter(T... values)
{
std::ostringstream out;
for (auto&& x : { values... }) {
out << x;
}
return out.str();
}
int main()
{
auto&& i = StringFormatter("One ", "two"); //Success
auto&& j = StringFormatter("one ", 1, "two", 2.0); //Fails
std::cout << i;
}
I know that the above code is failing because the initializer list accepts only single type arguments.
I have tried a recursive approach to achieve the above implementation, but no luck.
If you can suggest a better way to achieve this, it would be a great help.
You can achieve this with C++17's fold expression:
template <typename... T>
std::string StringFormatter(T... values)
{
std::ostringstream out;
(out << ... << values);
return out.str();
}
In short:
If you don't have a C++17 compiler, you can rely on the int array trick:
template <typename... T>
std::string StringFormatter(T... values) {
std::ostringstream out;
int arr[] = { 0, (out << values, void(), 0)... };
return out.str();
}
The apparently useless 0 at the start of the array is required in the case the parameter pack is empty because you can't instantiate an array of size 0. The void() is there to circumvent hypothetical operator, overloads.
The evaluation order is guaranteed and the compiler should be able to optimize away the array in the resulting binary.
In depth:
This technique is the pre-C++17 way of doing fold expressions. Basically we create an array of sizeof...(T) + 1 elements (all 0). The catch here is that we are using properties of the , operator to run the operation we want on each element of the parameter pack.
Let's forget about the parameter pack and the template for a moment.
When you do:
something, other_thing
Assuming there is no overload to the , operator, the statement is evaluated to other_thing. But that doesn't mean that something is ignored. Its value is just discarded in favor of other_thing. We are using that property for our little trick.
int x = 0;
int a[] = { 0, (++x, 0) }; // a is {0, 0}, x is 1
Now since you can overload operator,, we just add an additional statement to avoid this hypothetical overload:
(something, void(), 0)
Since operator, is a binary operator, an overloaded version of it cannot have only one argument. By adding a statement evaluating to void we are preventing any hypothetical overload to be picked and therefore are sure we end up with our 0.
The last step is to combine that with our parameter pack and perform pack expansion on the resulting statement:
(out << values, void(), 0)...
There are better ways to do it now (with a fold expression), but if you want to use the recursive approach, it can look something like this:
#include <sstream>
#include <string>
#include <iostream>
template <class T>
std::string stringify(T const &t) {
std::stringstream b;
b << t;
return b.str();
}
template<typename T, typename... Args>
std::string stringify(T arg, const Args&... args) {
return stringify(arg) + stringify(args...);
}
int main() {
std::string three{" three"};
std::cout << stringify("one: ", 1, " two: ", 2, three, "\n");
return 0;
}
You should be able to use this with essentially any type that supports stream insertion. If you're passing enough parameters that the quadratic time on the number of parameters is a concern, 1) go see a psychiatrist, and 2) feel free to use code more on this general order:
#include <sstream>
#include <string>
#include <iostream>
namespace detail {
template <class T>
void stringify(std::ostringstream &b, T const &t) {
b << t;
}
template<typename T, typename... Args>
void stringify(std::ostringstream &os, T arg, const Args&... args) {
stringify(os, arg);
stringify(os, args...);
}
}
template <typename ...Args>
std::string stringify(const Args &...args) {
std::ostringstream os;
detail::stringify(os, args...);
return os.str();
}
int main() {
std::string three{" three"};
std::cout << stringify("one: ", 1, " two: ", 2, three, "\n");
}
...but definitely see a psychiatrist first. If you're passing enough arguments for it to matter, you're clearly doing something horribly wrong.
I am a newbie in c++. I know this is a very common question, but I want a complete code to concat any number of strings which are passed to function in c++. I am calling the function as:
string var1,var2;
var1=concat_string("one","two");
cout<<var1<<endl;
var2=concat_string("one","two","three");
cout<<var2<<endl;
My required output is:
onetwo
onetwothree
I have read about variadic function, but I tried the following code to concatenate strings without worrying of the result size and number of string arguments. My code is:
#include <cstdarg>
template<typename... T>
string concat_string(T const&... t){
std::stringstream s;
s<<t;
return s;
}
But I got lots of error in this code. How can I correct my code. Thanks..
In C++17, with fold expression, it would be
template<typename... Ts>
string concat_string(Ts const&... ts){
std::stringstream s;
(s << ... << ts);
return s.str();
}
Previously (but since C++11), you have to rely on some trick to have a valid expansion context, such as:
template<typename... Ts>
string concat_string(Ts const&... ts){
std::stringstream s;
int dummy[] = {0, ((s << ts), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable
return s.str();
}
Since it looks like you're learning C++11, here's a minor extension of #Jarod42's excellent solution to support perfect forwarding:
template <typename... T>
std::string concat_string(T&&... ts) {
std::stringstream s;
int dummy[] = { 0, ((s << std::forward<T>(ts)), 0)... };
static_cast<void>(dummy); // Avoid warning for unused variable
return s.str();
}
Perfect forwarding and rvalue references is another feature in C++11 that can result in improved performance.
I am trying to implement a function which accepts a variable number of strings and forwards to a print function, which expects a char pointer and size for every string, interleaved.
Example:
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
I thought that the following should be a correct implementation, but even though it compiles the behavior is very surprising to me.
template <class ...Args>
void forward(const Args & ... args) {
doPrint( (args.c_str(), args.size())...);
}
forward(a, b) calls doPrint(3, 4), and not doPrint("123", 3, "1234", 4), as if I had written doPrint((args.size())...). The call to c_str() is ignored completely by the compiler.
I tried g++, clang, and icc with all yielding the same output. What is wrong with (args.c_str(), args.size())...?
Indeed, std::make_tuple(args.c_str(), args.size())... works as expected, but let's say I cannot change doPrint to accept and process tuples.
The comma operator is an expression whose value is the value of the last expression.
For example:
int a = (1, 2, 3, 4, 5, 6);
assert(a == 6);
What you can try instead is using tuples:
doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));
Then doPrint will need to be changed to work with a tuple; it could unpack the tuple back into a parameter pack if desired or just work with the tuple directly.
Example unpacking tuple:
template <class Tuple, std::size_t ... indices>
doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
{
doPrint(std::get<indices>(t)...);
}
template <class Tuple>
doPrint(Tuple t)
{
doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
There could be some problems with ambiguous function names so you may need to change the names of these helper functions, but hopefully this is enough for you to get going.
(args.c_str(), args.size()) is a comma-separated expression, meaning that only the last part (args.size()) will be passed to the function.
It will then repeat this for each parameter, so it will actually call doPrint just with the strings sizes!
You should change doPrint to use tuples instead, otherwise you have to use some crazy template meta-programming stuff.
I'd probably do it this way in order to avoid exposing tuples to the programming interface:
#include <string>
#include <utility>
#include <tuple>
extern void doPrint(...);
namespace detail {
template<std::size_t...Is, class Tuple>
void forward(std::index_sequence<Is...>, Tuple&& tuple)
{
doPrint(std::get<Is>(tuple)...);
}
}
template<class...Strings>
void forward(Strings&&... strings)
{
detail::forward(std::make_index_sequence<sizeof...(Strings) * 2>(),
std::tuple_cat(std::make_tuple(strings.data(), strings.size())...)
);
}
int main()
{
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
}
Jason Turner demonstrates a concise way to expand variadic templates using an initializer list in this video:
http://articles.emptycrate.com/2016/05/09/variadic_expansion_wrap_up.html
template< typename ... T >
void do_print(T ... args)
{
(void)std::initializer_list<int> {
(std::cout << args.c_str() << ": "
<< args.size() << "\n", 0)...
};
}
template< typename ... T >
void forward_print(T ... args)
{
do_print(args...);
}
int main(int argc, const char * argv[])
{
std::cout << "Hello, World!\n";
std::string a = "1234";
std::string b = "567";
forward_print(a, b);
return 0;
}
This works with g++ -std=c++11
For various reasons I am looking for a way to capture the constexpr-ness of arguments passed to a function. It's a bit tricky to explain so I think code best demonstrates what I like to achieve
#include <vector> // For std::size_t
#include <cstdio>
namespace
{
template<std::size_t N, typename ...TArgs>
constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
{
return i < N && s[i] != 0
? 1 + cstrlen (s, i + 1)
: 0
;
}
template<std::size_t N, typename ...TArgs>
inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
{
static_assert (cstrlen (format) > 0, "format must not be empty string");
printf (format, std::forward<TArgs> (args)...);
}
}
#define SILLY_PRINTF(format, ...) \
static_assert (cstrlen (format) > 0, "format must not be empty string"); \
printf (format, ##__VA_ARGS__);
int main()
{
// This works but relies on macros
SILLY_PRINTF ("Hello: %d", 1);
// This doesn't work
silly_printf ("Hello: %d", 1);
return 0;
}
I can't get silly_printf to work as I want it to. The compiler complains that the expression doesn't evaluate to a constant. We know it's constexpr when calling silly_print with a string literal but the constexpr-ness gets lost (I am using VS2015 here by the way).
I was thinking perhaps I can add constexpr to parameters (much like const) but no success there.
I can work-around this using macros (demonstrated by SILLY_PRINTF macro) but that feels like a failure.
Any ideas most welcome.
PS. What I am really is trying to achieve is slightly less silly
There's a GNU extension (supported by g++ and clang) that allows user defined literals of the form:
template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }
With this, one can build a constexpr-string type without macros that could be used like this:
constexpr auto str = "testing\0length"_string;
static_assert(str.strlen() == 7, "!");
By encoding all the string's properties into the type, you can then static_assert on it anywhere, constexpr or not.
For example, in your silly_printf:
template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
static_assert(format_string.strlen() > 0, "format string must not be empty");
printf(format_string.c_str(), args...);
}
And use it like this:
silly_printf("testing %d %s %x embedded\0null"_string, 1, "2", nullptr);
You could also use another operator"" _silly_printf() returning a function object to get a syntax like "format string"_silly_printf(args...).
See it live on Coliru
You don't need to use a char array reference as the argument. Here is one that I use but you need to have c++14 relaxed constexpr rules:
using size_t=decltype(sizeof(int));
namespace util
{
template<typename char_t>
constexpr size_t str_size(const char_t*)noexcept;
}
template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
const char_t* a_char=a_str;
while(*a_char!=char_t(0))
{
++a_char;
}
return size_t(a_char-a_str);
}
static_assert(util::str_size("hello")==size_t(5),"");
If you can't use c++14 a recursive version will work too. You still just use a char pointer as the argument rather than a char array reference.
Right now I use the following piece of code to dummily convert basic types (int, long, char[], this kind of stuff) to std::string for further processing:
template<class T>
constexpr std::string stringify(const T& t)
{
std::stringstream ss;
ss << t;
return ss.str();
}
however I don't like the fact that it depends on std::stringstream. I tried using std::to_string (from C++11's repertoire) however it chokes on char[] variables.
Is there a simple way offering an elegant solution for this problem?
As far as I know the only way of doing this is by specialising the template by the parameter type with SFINAE.
You need to include the type_traits.
So instead of your code use something like this:
template<class T>
typename std::enable_if<std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::to_string(t);
}
template<class T>
typename std::enable_if<!std::is_fundamental<T>::value, std::string>::type stringify(const T& t)
{
return std::string(t);
}
this test works for me:
int main()
{
std::cout << stringify(3.0f);
std::cout << stringify("Asdf");
}
Important note: the char arrays passed to this function need to be null terminated!
As noted in the comments by yakk you can get rid of the null termination with:
template<size_t N> std::string stringify( char(const& s)[N] ) {
if (N && !s[N-1]) return {s, s+N-1};
else return {s, s+N};
}
Is there a simple way offering an elegant solution for this problem?
Since nobody proposed it, consider using boost::lexical_cast.
This integrates seamlessly with anything that implements std::ostream<< operator and can be extended for custom types.
I'd recommend using enable_if_t and if you're going to take in any single character variables you specialize those:
template<typename T>
enable_if_t<is_arithmetic<T>::value, string> stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!is_arithmetic<T>::value, string> stringify(T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
Here I'm just specializing char. If you need to specialize wchar, char16, or char32 you'll need to do that as well.
Anyway for non-arithmetic types these overloads will default to using ostringstream which is good cause if you've overloaded the extraction operator for one of your classes this will handle it.
For arithmetic types this will use to_string, with the exception of char and anything else you overload, and those can directly create a string.
Edit:
Dyp suggested using whether to_string accepts an argument of T::type as my enable_if_t condition.
The simplest solution is only available to you if you have access to is_detected in #include <experimental/type_traits>. If you do just define:
template<typename T>
using to_string_t = decltype(to_string(declval<T>()));
Then you can set your code up as:
template<typename T>
decltype(to_string(T{})) stringify(T t){
return to_string(t);
}
template<typename T>
enable_if_t<!experimental::is_detected<to_string_t, T>::value, string> (T t){
return static_cast<ostringstream&>(ostringstream() << t).str();
}
template<>
string stringify<char>(char t){
return string(1, t);
}
I asked this question to figure out how to use to_string as my condition. If you don't have access to is_detected I'd highly recommend reading through some of the answers cause they are phenomenal: Metaprograming: Failure of Function Definition Defines a Separate Function
I believe, the most elegant solution is:
#include <string>
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
template <typename T>
typename std::enable_if<!std::is_constructible<std::string, T>::value, std::string>::type
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
Here, if we can construct std::string using T (we check it with help of std::is_constructible<std::string, T>), then we do it, otherwise we use to_string.
Of course, in C++14 you can replace typename std::enable_if<...>::type with much shorter std::enable_if_t<...>. An example is in the shorter version of the code, right below.
The following is a shorter version, but it's a bit less efficient, because it needs one extra move of std::string (but if we do just a copy instead, it's even less efficient):
#include <string>
std::string stringify(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
template <typename T>
std::enable_if_t<!std::is_convertible<T, std::string>::value, std::string>
stringify(T&& value) {
using std::to_string; // take advantage of ADL (argument-dependent lookup)
return to_string(std::forward<T>(value)); // take advantage of perfect forwarding
}
This version uses implicit conversion to std::string then possible, and uses to_string otherwise. Notice the usage of std::move to take advantage of C++11 move semantics.
Here is why my solution is better than the currently most voted solution by #cerkiewny:
It have much wider applicability, because, thanks to ADL, it is also
defined for any type for which conversion using function to_string
is defined (not only std:: version of it), see the example usage below.
Whereas the solution by #cerkiewny only works for the fundamental
types and for the types from which std::string is constructible.
Of course, in his case it is possible to add extra overloads of
stringify for other types, but it is a much less solid solution if
compared to adding new ADL versions of to_string. And chances are
height, that ADL-compatible to_string is already defined in a third party library for a
type we want to use. In this case, with my code you don't need to write any additional code at all to make stringify work.
It is more efficient,
because it takes advantage of C++11 perfect forwarding (by using universal references (T&&) and std::forward).
Example usage:
#include <string>
namespace Geom {
class Point {
public:
Point(int x, int y) : x(x), y(y) {}
// This function is ADL-compatible and not only 'stringify' can benefit from it.
friend std::string to_string(const Point& p) {
return '(' + std::to_string(p.x) + ", " + std::to_string(p.y) + ')';
}
private:
int x;
int y;
};
}
#include <iostream>
#include "stringify.h" // inclusion of the code located at the top of this answer
int main() {
double d = 1.2;
std::cout << stringify(d) << std::endl; // outputs "1.200000"
char s[] = "Hello, World!";
std::cout << stringify(s) << std::endl; // outputs "Hello, World!"
Geom::Point p(1, 2);
std::cout << stringify(p) << std::endl; // outputs "(1, 2)"
}
Alternative, but not recommended approach
I also considered just overloading to_string:
template <typename T>
typename std::enable_if<std::is_constructible<std::string, T>::value, std::string>::type
to_string(T&& value) {
return std::string(std::forward<T>(value)); // take advantage of perfect forwarding
}
And a shorter version using implicit conversion to std::string:
std::string to_string(std::string s) { // use implicit conversion to std::string
return std::move(s); // take advantage of move semantics
}
But these have serious limitations: we need to remember to write to_string instead of std::to_string everywhere where we want to use it; also it is incompatible with the most common ADL usage pattern:
int main() {
std::string a = std::to_string("Hello World!"); // error
using std::to_string; // ADL
std::string b = to_string("Hello World!"); // error
}
And it's most probable, there are other problems connected with this approach.
The simplest solution is to overload for the types you want:
using std::to_string;
template<size_t Size>
std::string to_string(const char (&arr)[Size])
{
return std::string(arr, Size - 1);
}
since to_string isn't a template you can't specialize it, but fortunately this is easier.
The code assumes the array is null terminated, but is still safe if it is not.
You may also want to put the using line inside the functions that call to_string if you have strong feelings about where using belongs.
This also has the benefit that if you pass it a non-null-terminated string somehow, it does not have UB as the one argument std::string constructor does.
Although the the question is not of a gimme the code kind, since I already have a solution implemented I thought of sharing it:
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string;
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string;
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string;
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string;
inline auto buildString() -> std::string { return {}; }
template <class... Tail>
inline auto buildString(std::string const &head, Tail const &... tail)
-> std::string {
return head + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char const *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class... Tail>
inline auto buildString(char *head, Tail const &... tail) -> std::string {
return std::string{head} + buildString(tail...);
}
template <class Head, class... Tail>
inline auto buildString(Head const &head, Tail const &... tail) -> std::string {
return std::to_string(head) + buildString(tail...);
}
Usage:
auto gimmeTheString(std::string const &str) -> void {
cout << str << endl;
}
int main() {
std::string cpp_string{"This c++ string"};
char const c_string[] = "this c string";
gimmeTheString(buildString("I have some strings: ", cpp_string, " and ",
c_string, " and some number ", 24));
return 0;
}