I'm playing around with variadic function templates in C++11 and have got the basic idea with code something like:
void helper()
{
std::cout << "No args" << std::endl;
}
template< typename T >
void helper( T&& arg )
{
size_t n = 0;
std::cout << "arg " << n << " = " << arg << std::endl;
helper();
}
template< typename T, typename... Arguments >
void helper( T&& arg, Arguments&& ... args )
{
size_t n = sizeof...( args );
std::cout << "arg " << n << " = " << arg << std::endl;
helper( args... );
}
However, what I want is for the argument number (the variable n in the code) to count up rather than down. How can I do this elegantly? I could write a wrapper function that creates a 'hidden' argument count but I feel there should be a neater way?
Thanks!
Do you mean something like this? I think I understand what you're looking for, but if not I'll not hesitate to drop this answer:
#include <iostream>
#include <utility>
void helper()
{
std::cout << "no args" << std::endl;
}
template<size_t N, typename T>
void helper(T&& arg)
{
std::cout << "arg " << N << " = " << arg << std::endl;
}
template<size_t N, typename T, typename... Args>
void helper(T&& arg, Args&&... args)
{
helper<N>(std::forward<T>(arg));
helper<N+1, Args...>(std::forward<Args>(args)...);
}
template<typename T, typename... Args>
void helper(T&& arg, Args&& ... args)
{
helper<0>(std::forward<T>(arg), std::forward<Args>(args)... );
}
int main()
{
helper();
std::cout << '\n';
helper("single");
std::cout << '\n';
helper("one", 2U);
std::cout << '\n';
helper(1,"two", 3.0, 4L);
std::cout << '\n';
return 0;
}
Output
no args
arg 0 = single
arg 0 = one
arg 1 = 2
arg 0 = 1
arg 1 = two
arg 2 = 3
arg 3 = 4
See it live
You may do the following: live example
#if 1 // Not in C++11 // make_index_sequence
#include <cstdint>
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
namespace detail
{
template<std::size_t...Is, typename... Ts>
void helper(index_sequence<Is...>, Ts&&...args)
{
int dummy[] = {0, ((std::cout << "arg " << Is << " = " << args << std::endl), 0)...};
static_cast<void>(dummy);
}
}
void helper()
{
std::cout << "No args" << std::endl;
}
template<typename ... Ts>
void helper(Ts&&... args)
{
detail::helper(make_index_sequence<sizeof...(Ts)>(), std::forward<Ts>(args)...);
}
Related
I have a small bit of code.
I want to know if i could cut out my ProcessArg function
I would like to loop the parameter pack from inside my call function.
Im i best to use a lambda function in initializer_list or something else, if so how do i do it.
Thanks
template <class R, class Arg>
R ProcessArg(Arg&& arg) {
std::cout << typeid(arg).name() << (std::is_reference<Arg>::value ? "&" : "") << std::endl;
//std::cout << std::boolalpha << std::is_reference<Arg>::value << std::endl; // not always true
return R();
}
template <typename R, typename... Args>
R CallFunction(Args&&... args) {
std::size_t size = sizeof...(Args);
std::initializer_list<R>{ProcessArg<R>(std::forward<Args>(args)) ...};
return R();
}
template<typename Fn> class FunctionBase;
template<typename R, typename... Args>
class FunctionBase <R(*)(Args...)> {
public:
FunctionBase() {}
R operator()(Args&&... args) { // Args&& is a universal reference
return CallFunction<R>(std::forward<Args>(args)...);
}
};
int foo(int a, int& b) {
std::cout << std::boolalpha << std::is_reference<decltype(a)>::value << std::endl; // falae
std::cout << std::boolalpha << std::is_reference<decltype(b)>::value << std::endl; // true
return a + b;
}
int main() {
int in = 10;
foo(1, in);
FunctionBase<decltype(&foo)> func;
func(1, in);
}
With c++17 fold-expressions, you can replace:
std::initializer_list<R>{ProcessArg<R>(std::forward<Args>(args)) ...};
with:
((std::cout << typeid(args).name()
<< (std::is_reference<Args>::value ? "&" : "")
<< std::endl
), ...);
Or with a lambda expression to improve readability:
auto process = [](auto&& arg) {
std::cout << typeid(arg).name()
<< (std::is_lvalue_reference<decltype(arg)>::value ? "&" : "")
<< std::endl;
};
(process(std::forward<Args>(args)), ...);
In c++20:
auto process = [] <typename Arg> (Arg&& arg) {
std::cout << typeid(arg).name()
<< (std::is_reference<Arg>::value ? "&" : "")
<< std::endl;
};
(process(std::forward<Args>(args)), ...);
#include <iostream>
using namespace std;
template<class... Ts>
void Fun(Ts... ts)
{
cout <<"TS: "; ((cout<<ts<<", "), ..., (cout<<endl));
}
int main()
{
Fun(1,'a',"blah", 2.13, 3.14f); //console: 1, a, blah, 2.13, 3.14,
}
I want to remove the , separator after 3.14, how can I do that?
I have tried cout<<"\b \b" and cout<<'\b', but it doesn't work.
You might do
template <class... Ts>
void Fun(Ts... ts)
{
const char* sep = "";
cout << "TS: "; (((cout << sep << ts), sep = ", "), ..., (cout << endl));
}
Demo
or even, as you have header:
template <class... Ts>
void Fun(Ts... ts)
{
const char* sep = "TS: ";
(((cout << sep << ts), sep = ", "), ..., (cout << endl));
}
Demo
Or you could do:
template<class T = const char *, class... Ts>void Fun(T first = "", Ts... ts)
{
cout << "TS: " << first; ((cout << ", " << ts), ..., (cout << endl));
}
template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
std::wostringstream woss;
owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all
//or
owss << Msg << ". " << args[0] << ": '" << args[1] << "' " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}
I know I can just use a list of pairs or something like that instead, but I'm interested in how to do this while keeping the syntax of the function to:
const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);
You can use a fold expression! It's not the prettiest*, but it's shorter than all the non-fold solutions presented:
template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
std::wostringstream owss;
owss << msg << ". ";
std::array<const char*, 2> tokens{": '", "' "};
int alternate = 0;
((owss << args << tokens[alternate], alternate = 1 - alternate), ...);
return owss.str();
}
Demo with sample output: https://godbolt.org/z/Gs8d2x
We perform a fold over the comma operator, where each operand is an output of one args and the alternating token, plus switching the token index (the latter two are combined with another comma operator).
*To a reader familiar with fold expressions (and the comma operator) this is probably the "best" code, but for everyone else it's utter gibberish, so use your own judgement whether you want to inflict this on your code base.
This is easy with a couple of helper functions that follow the following pattern.
void helper() {}
template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
do_single_pair(t1, t2);
helper(t...);
}
This is not a fold expression but the net result is the same.
I suppose you can try with an index and a ternary operator.
Something as follows
template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
{
std::wostringstream woss;
int i = 0;
((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));
return woss.str();
}
The following code should do the trick. The parameter pack is expanded in an initializer list.
#include <string>
#include <iostream>
#include <sstream>
#include <vector>
template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
auto argumentsVector = std::vector<std::string>{args...};
std::stringstream ss;
ss << msg << ". ";
for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";
auto result = ss.str();
if (!argumentsVector.empty())
result.pop_back();
return result;
}
int main()
{
std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}
With std::index_sequence:
template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
std::wstringstream woss;
woss << msg << ". ";
auto sep = L"";
((woss << sep << std::get<0>(pairs) << L": '"
<< std::get<1>(pairs) << L"'", sep = L" "), ...);
return woss.str();
}
template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}
template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
static_assert(sizeof...(Ts) % 2 == 0);
return descf_impl(msg,
std::make_index_sequence<sizeof...(Ts) / 2>(),
std::tie(ts...));
}
Demo
I am trying to create a general debug print function.
enum class DebugLevel : uint8_t
{
INFO = 0,
EVENT = 1,
WARNING = 2,
ERROR = 3,
CRITICAL = 4
};
DebugLevel generalDebugLevel = DebugLevel::INFO;
template <typename ...T>
void DPRINT (DebugLevel dbgLevel, T&& ...args)
{
if (dbgLevel >= generalDebugLevel)
{
std::cerr << __FILE__ << ":" << __LINE__ << " " << args... << std::endl;
}
}
As you can see, I need to unpack as I pass it to <<.
Any clues?
template <typename ...T>
void DPRINT (DebugLevel dbgLevel, T&& ...args)
{
if (dbgLevel >= generalDebugLevel)
{
std::cerr << __FILE__ << ":" << __LINE__ << " ";
using expander = int[];
(void)expander{0, (void(std::cerr << std::forward<T>(args) << " "),0)...};
std::cerr << std::endl;
}
}
This code has a partial user specializations for pointers and arrays.
When the array specialization is explicitly called, the expected value is returned.
However, when a variadic template function is used, the array parameter is converted to a pointer, and the pointer specialization is called.
Is there a way to get the compiler (g++ 4.8.1 in this case) to not do that cast?
Or is there a different way to return the "total size" that doesn't use template specializations?
#include <iostream>
template <typename T, typename... Params>
struct TestTemplate
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T);
}
};
template <typename T, typename... Params>
struct TestTemplate<T*, Params...>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T*>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (T);
}
};
template <typename T, size_t N, typename... Params>
struct TestTemplate<T[N], Params...>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return N * sizeof (T) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T, size_t N>
struct TestTemplate<T[N]>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return N * sizeof (T);
}
};
template <typename... Params>
size_t GetSizeof (Params... params)
{
return TestTemplate<Params...>::Sizeof();
}
struct TestType
{
double x = 0., y = 0.;
char buf[64];
};
int main (int, char *[])
{
std::cout << TestTemplate<int[10]>::Sizeof() << std::endl; // prints 40. OK
std::cout << GetSizeof (2, 3, 4) << std::endl; // prints 12. OK
TestType tt;
std::cout << GetSizeof (&tt, 1) << std::endl; // prints 84. OK
int int_arr[10];
std::cout << GetSizeof (int_arr, 1) << std::endl; // prints 8, want 41
}
You may replace your GetSizeof by: (https://ideone.com/jqXT4s)
template <typename... Params>
size_t GetSizeof (const Params&... params)
{
return TestTemplate<Params...>::Sizeof();
}
Once you have done that, you may simply use:
template <typename T, typename... Params>
struct TestTemplate
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (typename std::remove_pointer<T>::type) + TestTemplate<Params...>::Sizeof();
}
};
template <typename T>
struct TestTemplate<T>
{
static size_t Sizeof()
{
std::cout << __FILE__ << ':' << __LINE__ << std::endl;
return sizeof (typename std::remove_pointer<T>::type);
}
};
as sizeof(T[N]) == N * sizeof(T).