I want to be able to combine multiple different arguments into a single string using ostringstream. That way I can log the resulting single string without any random issues.
I got this far:
template <typename T>
void MagicLog(T t)
{
std::cout << t << std::endl;
}
template<typename T, typename... Args>
void MagicLog(T t, Args... args) // recursive variadic function
{
std::cout << t << std::endl;
MagicLog(args...);
}
template<typename T, typename... Args>
void LogAll(int logType, T t, Args... args)
{
std::ostringstream oss;
MagicLog(t);
MagicLog(args...);
//Log(logType, oss.str());
}
So I need to replace std::cout with the oss that I made in the LogAll function, I tried passing it as an argument to the other functions but it was complaining about a "deleted function"...
So: How can I get a recursive variadic function to accept another parameter, the ostringstream?
I don't really understand your problem. Just like what you did with your LogAll function, passing an ostream& as first parameter works like a charm:
#include <iostream>
#include <sstream>
template <typename T>
void MagicLog(std::ostream& o, T t)
{
o << t << std::endl;
}
template<typename T, typename... Args>
void MagicLog(std::ostream& o, T t, Args... args) // recursive variadic function
{
MagicLog(o, t);
MagicLog(o, args...);
}
template<typename... Args>
void LogAll(int logType, Args... args)
{
std::ostringstream oss;
MagicLog(oss, args...);
std::cout << oss.str();
}
int main()
{
LogAll(5, "HELLO", "WORLD", 42);
}
It was also possible to eliminate the duplicate code from your MagicLog function.
Related
I would like to create function calling another function and printing its arguments.
It should be compatible with many functions (returning the same result) with many combinations of variables of arguments.
I would like to have something like this:
int fun1(){}
int fun2(int i){}
int fun3(std::string s, int i){}
void execute_and_print(std::function f, ...)
{
///code
}
int main()
{
execute_and_print(&fun1);
execute_and_print(&fun2, 3);
execute_and_print(&fun3,"ff",4);
}
It could print:
executed function with arguments:
executed function with arguments: 3
executed function with arguments: ff, 4
Is it even possible in C++?
In C++17 it is very simple
template <typename F, typename... Args>
void execute_and_print(F f, Args... args)
{
(std::cout << ... << args);
f(args...);
}
Prior to that there is extra ceremony
template <typename F, typename... Args>
void execute_and_print(F f, Args... args)
{
int dummy[] = { (static_cast<void>(std::cout << args), 0)... };
f(args...);
}
It is not foolproof, but any possible errors will be caught at compile time (ie. the code will not compile). It should work file, as long as the provided parameters match those of the called function, and a matching << operator exists for each parameter.
template<class Fn, class...Args>
void execute_and_print(Fn fn, Args...args) {
int f[sizeof...(Args)] = { (std::cout << args << ", ", 0)... };
fn(args...);
}
Refer to https://en.cppreference.com/w/cpp/language/parameter_pack. The sizeof... command is actually the number of elements, not their combined size.
You can use templates to get it done,
template <class... Args>
void RunThrough(Args&& ... args)
{
([&](auto& input)
{
std::cout << input << ", ";
} (args), ...);
}
template<class Func, class... Args>
decltype(auto) execute_and_print(Func f, Args&&... args)
{
f(args...);
std::cout << "executed function with arguments: ";
RunThrough(args...);
std::cout << std::endl;
}
You can use lambdas, std::function objects and function pointers in this.
Reference: https://stackoverflow.com/a/60136761/11228029
I am new to template meta programming. I want to strip args from variable argument in c++. I am making a function which will push_back() element to any type of container. Its very easy to do in C++ 17 but i want to provide support for C++ 11 . Please find the code below push_back() function implementation i am looking for. Please avoid va_start(), va_end() c style solution.
#include <iostream>
#include <vector>
template<class Container, class T, class... Args>
void push_back(Container& con, T tail, Args... args);
template<class T>
T get_tail(T data) {
return data;
}
template<class T, class ...Args>
T get_tail(T& tail, Args&... args) {
return tail;
}
template<class Container , class T,class... Args>
void push_back(Container& con, T tail,Args... args ) {
//C++ 17 ((con.push_back(args), ...);
con.push_back(tail);
std::cout << (tail) << std::endl;
T newTail = get_tail(args...);
push_back(con,newTail,args...);
}
template<typename T, typename... Args>
bool pair_comparer(T a, T b, Args... args) {
return a == b && pair_comparer(args...);
}
int main()
{
std::vector<int> v_int;
push_back(v_int,1,2,3,4 );
std::cout << "Hello World!\n";
for (auto iter = v_int.begin(); iter != v_int.begin(); iter++) {
std::cout << "=== " << *iter << " ===" << std::endl;
}
}
You've already stripped the first argument away when you did
void push_back(Container& con, T tail, Args... args ) {
tail is the first argument and args is the rest, so your recursive call at the end simply needs to say
pus_back(con, args...)
No need for any of this get_tail hackery. Then, simply have another overload of push_back that looks like this.
template <class Container>
void push_back(Container& con) {
// A whole lot of nothing happens here...
}
That handles the base case, and the function you already wrote (with the one minor modification) handles the recursive case.
Not sure to understand what do you exactly want but...
Are you sure you need recursion?
You can develop something similar the C++17 way unpacking the variadic args..., using the initialization of an unused C-style array, as follows
template <typename Container, typename ... Args>
void push_back (Container & con, Args ... args ) {
using unused = int[];
(void)unused { 0, ((void)con.push_back(args), std::cout << args << std::endl, 0)... };
}
Or simply as follows
template <typename Container, typename ... Args>
void push_back (Container & con, Args ... args ) {
using unused = int[];
(void)unused { 0, ((void)con.push_back(args), 0)... };
}
if the std::cout part is just for debugging purposes.
The problem in your code is that you call push_back inside of itself with the same number of arguments. This makes the recursion infinite. Instead you need to "strip" one argument each time. And certainly provide the recursion base. Like this:
template<class Container>
void push_back(Container& con) {
}
template<class Container , class T,class... Args>
void push_back(Container& con, T tail, Args... args ) {
con.push_back(tail);
std::cout << (tail) << std::endl;
push_back(con, args...);
}
int main()
{
std::vector<int> v_int;
push_back(v_int,1,2,3,4 );
std::cout << "Hello World!\n";
for (auto x : v_int)
std::cout << "=== " << x << " ===" << std::endl;
}
This question already has answers here:
How to make a variadic macro for std::cout?
(4 answers)
Closed 3 years ago.
I want to create a C++ Template for a function, that allows per default intinity arguments of any datatypes. I found some examples where they try to Code the printf function but it doesn't work (they included stdarg.h, i want something like that:)
//util.cpp
#include <iostream>
template<typename ...Args>
void debugPrint(Args... args)
{
// pseudo: foreach args as a:
std::cout << a;
// pseudo: endforeach
std::cout << std::endl;
}
//util.hpp
template<typename ...Args> //?
void debugPrint(Args...);
//app.cpp
#include "util.hpp"
int main()
{
debugPrint("Hallo", " ", "Welt", 1, 2, "\t", 2.3);
return 0;
}
Want the consoleoutput:
Hallo Welt12 [TAB] 2.3
Then there were an examplte with stdarg.h
#include <stdarg.h>
void tprintf(const char* format) // base function
{
std::cout << format;
}
template<typename T, typename... Targs>
void tprintf(const char* format, T value, Targs... Fargs) // recursive function
{
for (; *format != '\0'; format++) {
if (*format == '%') {
std::cout << value;
tprintf(format + 1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
So how? :(
Thanks for any answere <3
As with any recursive solution, you need a base case to terminate the recursion. In this situation the base case is when there are no arguments left to print, in which case you do nothing:
void debugPrint() { }
Then the recursive template function, which processes a single "first" argument and then recurses on the remaining variadic arguments:
template <typename FirstArg, typename ...Args>
void debugPrint(FirstArg arg, Args... args)
{
std::cout << arg;
debugPrint(args...);
}
Tying it all together:
#include <iostream>
void debugPrint() { }
template <typename FirstArg, typename ...Args>
void debugPrint(FirstArg arg, Args... args)
{
std::cout << arg;
debugPrint(args...);
}
int main()
{
debugPrint("Hallo", " ", "Welt", 1, 2, "\t", 2.3);
}
Outputs:
Hallo Welt12 2.3
You can use fold expressions to do that (C++17):
template<typename... Args>
void debugPrint(Args const&... args) {
(std::cout << ... << args);
}
It will expand args with a << between expanded expressions. There is no need for recursion.
Here's a live example.
I have a std::tuple, and I want to unwrap the contents using std::index_sequence in order to call a variadic function template
Consider the following example code:
#include <iostream>
#include <tuple>
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename Tuple, std::size_t... Ixs>
void call_foo(const std::string& s, Tuple& t, std::index_sequence<Ixs...>)
{
foo(s, std::get<Ixs>(t)...);
}
template<typename... Ts>
struct Bar
{
Bar(Ts... ts) : t(ts...)
{ }
void do_it()
{
call_foo("hi", t, std::make_index_sequence<std::tuple_size<decltype(t)>::value>{});
}
std::tuple<Ts...> t;
};
template<typename... Ts> Bar<Ts...> make_bar(Ts... ts) { return Bar<Ts...>(ts...); }
int main ()
{
auto bar = make_bar(1, 'a', 2.3);
bar.do_it();
}
Note that I have to call through call_foo with my index_sequence in order to "unwrap" the index_sequence for calling std::get...
Is it possible to forego the intermediate call_foo function, and call foo directly?
That is, unwrap the tuple directly at the call-site?
If you don't want to or can't use std::apply, I can suggest a few alternatives.
The snippets below are based on the previous revision of your question that didn't have the class Bar in it. The same solutions work for the new revision as well.
(1) You could replace call_foo with a C++20 lambda with an explicit template parameter list:
#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename... Ts>
void bar(Ts... ts)
{
const std::string s = "hello world";
const auto t = std::make_tuple(ts...);
[&]<std::size_t ...I>(std::index_sequence<I...>)
{
foo(s, std::get<I>(t)...);
}
(std::make_index_sequence<std::tuple_size_v<decltype(t)>>{});
}
int main()
{
bar(1, 'a', 2.3);
}
Try it live
Unfortunately, GCC 8 currently seems to be the only major compiler that supports those.
(2) If your compiler doesn't have the new fancy lambdas, or you don't want to write the index_sequence boilerplate every time you need to expand the tuple, I suggest following:
#include <cstddef>
#include <iostream>
#include <tuple>
#include <utility>
template <std::size_t ...I, typename F> void with_sequence_impl(F &&func, std::index_sequence<I...>)
{
func(std::integral_constant<std::size_t, I>{}...);
}
template <std::size_t N, typename F> void with_sequence(F &&func)
{
with_sequence_impl(std::forward<F>(func), std::make_index_sequence<N>{});
}
template<typename... Ts>
void foo(const std::string& s, Ts... ts)
{
std::cout << "foo called with " << s << " and " << sizeof...(Ts) << " ts\n";
}
template<typename... Ts>
void bar(Ts... ts)
{
const std::string s = "hello world";
const auto t = std::make_tuple(ts...);
with_sequence<std::tuple_size_v<decltype(t)>>([&](auto ... i)
{
foo(s, std::get<i.value>(t)...);
});
}
int main()
{
bar(1, 'a', 2.3);
}
Try it live
I'd like to use parameters pack, but find the problem.
Some code:
template <typename Function, typename... Args>
auto f(Function func, Args... args) -> decltype(func(args...))
{
auto f11 = std::bind(func, args...);
f11();
}
void print(const char* string)
{
std::cout << string << std::endl;
}
All of this works well:
f([] (const char* additional, const char* more) {
std::cout << "hello ( " << additional << ", " << more << " )" << std::endl;
}, "additional text", "and one more");
auto printFunction = std::bind(&print, std::placeholders::_1);
printFunction("hello from print bind");
f(print, "hello from print directly");
but if i would like to give std::function to parameters pack:
f([] (std::function<void(const char*)> printParamFunc) {
printParamFunc("hello from print from std::function");
}, printFunction);
application no more compiles.
So, what the problem to use function as parameter in pack?
Thanks.
UPDATE:
if change code of f to:
template <typename Function, typename... Args>
auto f(Function func, Args... args) -> decltype(func(args...))
{
func(args...);
}
it works well, but i wouldn't like to execute this function here, i wanna create function and pass it like param.
UPDATE2:
Code execution example: http://ideone.com/gDjnPq
UPDATE3:
Clear code with compilation error: http://ideone.com/50z7IN
I understand the situation a bit better now.
Problem can be retroduced by this code:
void print(const char* string)
{
std::cout << string << std::endl;
}
int main(int argc, char ** argv)
{
auto lambda = [] (std::function< void ( const char * ) > printParamFunc) {
printParamFunc("hello from lambda!");
};
std::bind(lambda, std::bind(print, std::placeholders::_1))();
}
Nested std::bind trying to evaluate and fails with conversion _1 to const char *. It is specific of std::bind.
We need analogue of boost::bind::protect -- functor storing other functor -- and it is solve problem:
template <class F>
struct lazy_evaluate {
typedef typename F::result_type T;
explicit lazy_evaluate(F f) : f_(f) {}
template <class... Args>
T operator()(Args&&... args)
{
f_(std::forward<Args>(args)...);
}
private:
F f_;
};
template <class F>
lazy_evaluate<F> lazy(F f)
{
return lazy_evaluate<F>(f);
}
nested std::bind now looks like:
std::bind(lambda, lazy(std::bind(print, std::placeholders::_1)))();
and works well.