printf like utility in c++ without format specifier? - c++

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.

Related

Create string according to the parameter pack arguments count

I have a function with a parameter pack, I pass this pack to the fmt::format function, and I want to create a formatStr according to the args count, meaning add "{}#" for each passed argument.
I can do it using iterating, but is it possible to do this with one line solution?
How to do it gracefully?
template <typename... Args>
void formatArgs( Args&&...args)
{
const auto size = sizeof...(Args);
std::string formatStr = ...// "{}#{}#{}..." - {}# should depend on args count
/*
std::ostringstream formatStr;
for (const auto& p : { args... })
formatStr << {}#";
*/
auto res = fmt::format(formatStr.c_str(), args...);
...
}
An ugly fold expression would work. It's only redeeming quality is that it's just one line, and in C++20 it should be a constexpr, so in C++20 this'll wind up to be a single std::string constructor call:
#include <string>
#include <iostream>
template<typename ...Args>
std::string string_from_args(Args && ...args)
{
return std::string{ ((args, std::string{"%s#"}) + ...) };
}
int main()
{
std::cout << string_from_args(1, 2, 3, 4) << "\n";
return 0;
}
If you want to create a string with all the arguments I suggest you use an std::ostringstream to create the string directly instead of going through std::format or similar.
How to create the string differs between version of C++. Before C++17 and pre-fold you can use overloading of functions to handle the argument packs:
// Handle single-argument string construction
template<typename Arg>
std::string string_from_args(Arg&& arg)
{
return (std::ostringstream() << arg).str();
}
// Handle multiple-argument string construction
template<typename First, typename ...Rest>
std::string string_from_args(First&& first, Rest&& ...rest)
{
return (std::ostringstream() << string_from_args(first) << '#' << string_from_args(rest...)).str();
}
Then with fold-expressions introduced in C++17:
template<typename ...Args>
std::string string_from_args(Args&& ...args)
{
char const* separator = "";
std::ostringstream os;
(((os << separator << args), separator = "#"), ...);
return os.str();
}
With a simple main function
int main()
{
std::cout << string_from_args(123, 456.789, "foo", 'b');
}
both variants should construct and print the string
123#456.789#foo#b
If all arguments have the same type which seems to be the case considering that your current solution involves iteration then you could use fmt::join. For example:
#include <fmt/ranges.h>
template <typename... Args>
std::string formatArgs(Args&&... args) {
return fmt::format("{}", fmt::join({args...}, ""));
}
int main() {
fmt::print("{}", formatArgs(1, 2, 3));
}
prints
123

integer increment inside pack expansion

I have two functions row and col. row is a wrapper for col and should pack the return types to a tuple.
Something like this
#include <iostream>
#include <tuple>
template<typename T>
T col(size_t i)
{
return T(i);
}
template<typename ...Ts>
auto row()
{
size_t i = 0;
return std::make_tuple(col<Ts>(i++)...); //<-- undefined behaviour
}
int main()
{
auto m_row = row<int,int,double>(); //should expand to std::make_tuple(col<int>(0),col<int>(1),col<double(2));
std::cout << "std::get<0>(m_row)-" << std::get<0>(m_row) << std::endl;
std::cout << "std::get<1>(m_row)-" << std::get<1>(m_row) << std::endl;
std::cout << "std::get<2>(m_row)-" << std::get<2>(m_row) << std::endl;
return 0;
}
My problem is the integer i which has to be incremented inside the expansion from 0 up to sizeof...(Ts). I have considered index of the current type but this is not working if the types are not unique. I lack of other ideas, any help would be appreciated.
Using std::index_sequence_for we can achieve a moderately simple (but not as simple as I had hoped) solution.
As #NathanOliver mentioned, it requires a level of indirection because we need to inform a helper function of the index sequence. The top level function now looks like this:
template <typename... Ts>
auto row() {
return make_row(std::tuple<Ts...>{},
std::index_sequence_for<Ts...>{});
}
So the helper function takes a default constructed tuple of the type requested, and the compile time sequence of integers.
All the helper needs to do now is to construct a Tuple using the index sequence (0, 1, ...).
template <typename Tuple, std::size_t... Is>
auto make_row(Tuple, std::index_sequence<Is...>) {
return Tuple{ Is... };
}
Finally, to verify this does what we wanted:
int main()
{
auto r = row<int,int,double>();
static_assert(std::is_same<decltype(r), std::tuple<int, int, double>>::value);
}

Parameter pack expansion within parentheses gives bizarre output

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

how to pass n number of arguments to a function where n is not known [duplicate]

This question already has answers here:
Having trouble passing multiple initializer lists to variadic function template
(4 answers)
Closed 8 years ago.
I need to pass n number of arguments to a function. the user can enter as many arguments as he wants but we don't know the number of arguments he will pass. but all the implementations I have seen using va_list includes a count , but here we dont know the number. it is looks like
void xyz(int x[],...);
where we have only arrays
the function is used like.
xyz({1,2,3},{1,1},{2},{3,3,3})
and then I want 3 arrays in my functions in separate variables if possible. I need to do some computation in these arrays. Is this possible in c++??
You can use a variadic function (like printf), but you generally prefer not to.
You can take an initializer_list as a parameter. This will let you take a braced list of items that can all be converted to a single type:
void f(std::initializer_list<int> l);
...which you could call something like:
f({1, 2, 3, 4});
There's also a ctor for std::vector that takes an std::initializer_list, so you can take a (reference to) a vector<T> and accomplish roughly the same. Note, however, that (unlike most parts of C++) this doesn't support narrowing conversions, so for the f above that requires ints, you could get an error if you tried to pass (for example) a double instead.
If you don't like the braces, or want to support the parameters being of different types, you can use a variadic template instead. For example here's a function I posted some time ago to take an arbitrary number of parameters of (nearly) arbitrary type, put them together into a string, and write the resulting string to a socket:
#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...);
}
template<typename... Args>
void send_cmd(const Args&... args) {
std::string buffer = stringify(args...);
send(sock, buffer.c_str(), buffer.length(), 0);
}
int main() {
std::string three{" three"};
send_cmd("one: ", 1, " two: ", 2, three, "\n");
return 0;
}
#include <iostream>
#include <initializer_list>
#include <list>
#include <vector>
#include <algorithm>
#include <iterator>
// Base case: do nothing once all inputs have been processed
void implementation(const std::list<int>& acc) {}
// Recursively pick off one sequence at a time, copying the data into
// the accumulator
template<class ONE, class ... REST>
void implementation(std::list<int>& accumulator,
const ONE& first,
const REST& ... rest) {
std::copy(begin(first), end(first), std::back_inserter(accumulator));
implementation(accumulator, rest...);
}
// Interface, hiding the creation of the accumulator being fed to the
// template-recursive implementation.
template<class ... REST>
std::vector<int> concatenate(const std::initializer_list<REST>& ... rest) {
std::list<int> accumulator;
implementation(accumulator, rest...);
return std::vector<int>(begin(accumulator), end(accumulator));
}
template<class SEQ>
void show_contents(const SEQ& s) {
std::copy(begin(s), end(s), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
int main() {
show_contents(concatenate({1,2}, {3,4,5}, {6,7}));
show_contents(concatenate({8,9}));
show_contents(concatenate({9,8,7}, {6,5}, {4,3}, {2,1}));
}
If all the arguments are of the same type, you could pass a std::vector:
void xyz(std::vector<int>& parameters)
{
//...
}
In your example, it looks like the each parameter may have different quantities of numbers.
This can be handled by using a std::vector< std::vector< int> >:
void abc(std::vector< std::vector< int> >& parameters)
{
// Each slot of the outer vector is denoted by your {...} syntax.
std::vector<int>& parameter2 = parameters[1];
// Each number within {...} is represented by a vector of integers
std::cout << "{"
<< parameter2[0] << ", "
<< parameter2[1] << ", "
<< parameter2[2]
<< "}\n";
}
Edit 1: Passing parameters
You can place the numbers into variables and pass the variables to the function:
int main(void)
{
// Load up the individual parameters.
std::vector<int> p1 = {1};
std::vector<int> p2 = {2, 3};
std::vector<int> p3 = {5, 6, 7};
// Place all parameters into one container
std::vector< std::vector< int > > parameters = {p1, p2, p3};
// Call the function with the parameters
abc(parameters);
return 0;
}

How does this variable argument notation work?

In C++ I can define a function with a variable number of arguments like this:
void a(int a...) {
std::cout << a << std::endl;
}
And call it like this:
a(100, 200, 300);
However, apparently I can only access the first argument: The output of the call is 100.
How do I access the other arguments with this notation?
Your syntax is ... unfortunate, and refers to a C-style vararg function.
In C++11 you should prefer variardic templates. The easiest approach is something like this:
First, some helper code:
#include <utility>
template<typename Lambda>
void for_each_arg( Lambda&& unused ) {}
template<typename Lambda, typename Arg1, typename... Args>
void for_each_arg( Lambda&& closure, Arg1&& arg1, Args&&... args ) {
closure( std::forward<Arg1>(arg1) );
for_each_arg( std::forward<Lambda>(closure), std::forward<Args>(args)... );
}
now, we use it:
#include <iostream>
template<typename... Args>
void foo( Args&&... args ) {
for_each_arg( [](int x){
std::cout << x << "\n";
}, std::forward<Args>(args)... );
}
int main() {
foo( 1, 2, 3 );
}
and we can access each argument, and ensure that they convert to int. Note that the conversion to int is deferred until call of the body of for_each_arg.
If you use a var args interface you need to be able to tell from the named parameters how many arguments where provided in total. For example, the <stdio.h> function do that by having the format string be the last named argument followed by as many arguments as specified in the argument list. To access the arguments you need to use the various va_... functions and types.
You are much better off using variadic templates:
template <typename... T>
void f(T... a) {
// just expand the parameter pack over here
}
Sample code using your function a:
#include <iostream>
#include <cstdarg>
void a(int a...)
{
va_list args;
va_start(args, a);
int b = va_arg(args, int);
int c = va_arg(args, int);
std::cout << a << ", " << b << ", " << c << std::endl;
}
int main()
{
a(100, 200, 300);
return 0;
}
The variable argument syntax does not know the number or type of the parameters. Therefore something in the parameter list must indicate the number and possibly the type of the parameters. There are several methods that are typically used to determine the number of parameters:
The first parameter is the number of parameters.
The last parameter is a delimiter, a NULL or other unique value, for example.
The first parameter has other embedded information that determines the number, order and type of the parameters, for example the format parameter for printf.
In this example, I've simply assumed there are three parameters. Calling a with more or less parameters will cause undefined behavior (read, random results, to a crash).
Another variadic solution:
template<typename T, typename... Vs>
void print(T&& t, Vs&&... vs) {
std::cout << std::forward<T>(t) << std::endl;
int sink[] { (std::cout << " " << std::forward<Vs>(vs) << std::endl, 0)... };
(void)sink; // silence "unused variable" warning
}
Which has the benefit of not requiring helpers. We use pack expansion to forward each argument one at a time to cout. For syntax reasons, I leverage the comma operator so that the expression (cout..stuff.., 0) resolves to an integer, which we then discard into an array; this lets us use the pack expansion operator around our complex statement.
A solution which enforces the type int.
But with a usage a little different
#include <initializer_list>
#include <iostream>
// Or you may use std::vector
void print(const std::initializer_list<int>& a) {
for (auto elem : a) {
std::cout << elem << std::endl;
}
}
int main(int argc, char *argv[])
{
print({1, 2, 3}); // extra braces.
return 0;
}