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
Related
I am writing a function that stores a parameter pack into a std::any of tuple, and I need to allocate a pointer array void* arguments[] to each item of the tuple. How can I do that at compile time?
template <typename... ArgsT>
void func(ArgsT&&... args) {
// any will be the storage for the argument
std::any any = std::make_tuple(std::forward<ArgsT>(args)...);
// here I need a pointer array with each ptrs[i] to (void*)&std::get<i>( std::get<WhatTupleTypeHere?>(any) )
void* arguments[sizeof...(ArgsT)] = { ??? };
}
For example, if I have:
fun(1, 2.4, std::string("hello"))
Then arguments should go as follows:
std::any storage = std::tuple<int, float, std::string>(1, 2.4, "hello");
auto& tuple = std::tuple<int, float, std::string>>(storage);
void* arguments[3] = {
(void*)&std::get<0>(tuple),
(void*)&std::get<1>(tuple),
(void*)&std::get<2>(tuple)
};
My compiler supports c++17.
Full example including test cases with a few minor changes (e.g., static in func) to help with testing/lifetime issues, but I think I hit your requirements.
#include <any>
#include <iostream>
#include <string>
#include <tuple>
namespace {
namespace detail {
template <std::size_t INDEX, typename ...Ts>
void fill_array(void ** arguments, std::tuple<Ts...> & data) {
if constexpr(INDEX < sizeof...(Ts)) {
arguments[INDEX] = (void *)&std::get<INDEX>(data);
fill_array<INDEX + 1>(arguments, data);
}
}
}
template <typename... ArgsT>
void** func(ArgsT&&... args) {
// any will be the storage for the argument
static std::any any = std::make_tuple(std::forward<ArgsT>(args)...);
// here I need a pointer array with each ptrs[i] to (void*)&std::get<i>( std::get<WhatTupleTypeHere?>(any) )
static void* arguments[sizeof...(ArgsT)];
detail::fill_array<0>(arguments, std::any_cast<std::tuple<ArgsT...>&>(any));
return arguments;
}
}
int main() {
auto arguments = func(1, 2.4, std::string("hello"));
std::cout << *((int*)arguments[0]) << '\n'
<< *((double*)arguments[1]) << '\n'
<< *((std::string*)arguments[2]) << '\n';
return 0;
}
The trick is to use fill_array since we need compile-time constants when using std::get. We'll extract the tuple from your any, and then it's fairly straightforward code as far as templates go.
In the following code :
void print()
{
// This is our base case fn
;; // Do nothing
}
template <typename type1, typename... argspack>
void print(type1 a, argspack... args_rest)
{
cout << a << ((sizeof...(args_rest) != 0) ? "," : "\n");
print(args_rest...); // I guess this recursive call is inevitable
}
If the recursive call to variadic function is inevitable, the base case function is also inevitable. If so, is there a language feature, perhaps one
that comes with modern c++, that help a programmer get away without writing a base case function?
Another (slightly elaborate) way which avoids recursion and a trailing comma:
#include <iostream>
#include <tuple>
struct linefeed {};
template<typename...Args>
void print(Args&&... args)
{
const char* sep = "";
auto print_with_sep = [&sep](auto& os, auto& arg)
{
if constexpr (std::is_same<std::decay_t<decltype(arg)>, linefeed>())
{
sep = "";
os << '\n';
}
else
{
os << sep << arg;
sep = ",";
}
};
auto print_all = [&](auto&&...things)
{
(print_with_sep(std::cout, things), ...);
};
print_all(args..., linefeed());
}
int main()
{
print(1,2,3,4,5, "hello");
print("world", 5,4,3,2,1);
}
expected output:
1,2,3,4,5,hello
world,5,4,3,2,1
https://coliru.stacked-crooked.com/a/770912eee67d04ac
A dummy is one way. Another is to make it a single argument function that actually does work:
template<typename T>
void print(T a)
{
std::cout << a;
}
template <typename type1, typename... argspack>
void print(type1 a, argspack... args_rest)
{
print(a);
std::cout << ((sizeof...(args_rest) != 0) ? "," : "\n");
print(args_rest...); // I guess this recursive call is inevitable
}
A benefit to this approach is that it provides a customization point too. If some type wants to provide its own print implementation, all it has to do is write the overload. ADL will find it and overload resolution will favor it.
To be perfectly clear, you have to write the base case yourself. The language doesn't consider those two functions as related beyond being overloads of the same name. It isn't even aware there needs to be a base case, that is our logical requirement.
You may refer to Fold Expression which is supported as of C++17.
I came up with the code almost similar to your code but it has a trailing comma.
template<typename... argspack>
void print(argspack&&... args) {
((cout << args << ","), ...) << "\n";
}
I am not sure there is a way to get exactly the same with your code using Fold Expression. Since we sizeof...(args) is always the initial size in this version.
With one function in C++11:
template <typename... Ts>
void print(Ts... args)
{
const char* sep = "";
const int dummy[] = {((std::cout << sep << args), (sep = ", "), 0)..., 0};
static_cast<void>(dummy); // Avoid warning for unused variable
std::cout << "\n";
}
dummy array trick can be replaced by fold expression in C++17:
template <typename... Ts>
void print(Ts... args)
{
const char* sep = "";
(((std::cout << sep << args), (sep = ", ")), ...);
std::cout << "\n";
}
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 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
I am a little confused about how can I read each argument from the tuple by using variadic templates.
Consider this function:
template<class...A> int func(A...args){
int size = sizeof...(A);
.... }
I call it from the main file like:
func(1,10,100,1000);
Now, I don't know how I have to extend the body of func to be able to read each argument separately so that I can, for example, store the arguments in an array.
You have to provide overrides for the functions for consuming the first N (usually one) arguments.
void foo() {
// end condition argument pack is empty
}
template <class First, class... Rest>
void foo(First first, Rest... rest) {
// Do something with first
cout << first << endl;
foo(rest...); // Unpack the arguments for further treatment
}
When you unpack the variadic parameter it finds the next overload.
Example:
foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax
Then next level down we expand the previous Rest and get:
foo(true, Rest = { 'a', "hello"}); // First = bool
And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).
Storing the pack if different types
If you want to store the entire argument pack you can use an std::tuple
template <class... Pack>
void store_pack(Pack... p) {
std::tuple<Pack...> store( p... );
// do something with store
}
However this seems less useful.
Storing the pack if it's homogeneous
If all the values in the pack are the same type you can store them all like this:
vector<int> reverse(int i) {
vector<int> ret;
ret.push_back(i);
return ret;
}
template <class... R>
vector<int> reverse(int i, R... r) {
vector<int> ret = reverse(r...);
ret.push_back(i);
return ret;
}
int main() {
auto v = reverse(1, 2, 3, 4);
for_each(v.cbegin(), v.cend(),
[](int i ) {
std::cout << i << std::endl;
}
);
}
However this seems even less useful.
If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):
template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
T arr[sizeof...(args) + 1] = { first, args...};
}
int main()
{
foo(1);
foo(1, 10, 100, 1000);
}
If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).
Edit:
If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:
#include <vector>
#include <iostream>
#include <iterator>
template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
return std::reverse_iterator<Iter>(it);
}
template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{
return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}
int main() {
auto v = reverse({1, 2, 3, 4});
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
}
For sticking into an array if the arguments have different types, you can use also std::common_type<>
template<class ...A> void func(A ...args){
typedef typename std::common_type<A...>::type common;
std::array<common, sizeof...(A)> a = {{ args... }};
}
So for example, func(std::string("Hello"), "folks") creates an array of std::string.
If you need to store arguments in the array you could use array of boost::any as follows:
template<typename... A> int func(const A&... args)
{
boost::any arr[sizeof...(A)] = { args... };
return 0;
}