I wrote a simple log function by c++0x variadic templates,
but there are templates deduction error,could somebody help me, thanks!
#ifndef SLOG_H
#define SLOG_H
enum LOGLEVEL{INFO, DEBUG, ERROR};
/* static */ const char* g_loglevel[] = {"INFO", "DEBUG", "ERROR"};
template <class T>
void slog_(const T& v) {
std::cout << v;
}
template <class T, class... Args>
void slog_(const T& v, const Args&... args) {
std::cout << v;
slog_(args...);
}
template <class T, class... Args>
void slog(LOGLEVEL level, const Args&... args) {
time_t t;
struct tm tm;
char buf[32];
time(&t);
localtime_r(&t, &tm);
strftime(buf, sizeof(buf), "%F %T", &tm);
std::cout << "[" << g_loglevel[level] << "][" << buf << "] ";
slog_(args...);
}
#endif
In call function:
slog(INFO, "hello, number ", n, ", next is ", n + 1);
then compile error:
main.cpp:6:53: error: no matching function for call to 'slog(LOGLEVEL, const char [15], int&, const char [11], int)'
main.cpp:6:53: note: candidate is:
In file included from main.cpp:2:0:
slog.h:19:6: note: template<class T, class ... Args> void slog(LOGLEVEL, const Args& ...)
slog.h:19:6: note: template argument deduction/substitution failed:
main.cpp:6:53: note: couldn't deduce template parameter 'T'
Thanks!
Change template <class T, class... Args> to template <class... Args> for slog. As the error message says: couldn't deduce template parameter 'T' because you never use it as a function parameter.
Related
Problem Statement
I'm trying to pass in a struct that contains a generic attribute like such
template <typename Value>
struct ColumnValue {
std::string columnName;
Value value;
};
I'd also like to create a function that accepts an unknown number of parameters as such
print(T... args)
These args will be of the type ColumnValue objects with 1 or more...
I'd like the print function to do different things depending on what type "Value" is.
Desired Result
222
"hellooooo"
Code
#include <iostream>
template <typename Value>
struct ColumnValue {
std::string columnName;
Value value;
};
template <template<typename> typename ...X, typename ...Y>
void print(std::string firstArg, const X<Y>& ...args) {
for(auto val : {args...}) {
std::cout << val.value << std::endl;
}
}
int main() {
ColumnValue<int> v{
.columnName="hello",
.value=222
};
ColumnValue<std::string> d{
.columnName="hello",
.value="hellooooo"
};
print("", v, d);
return 0;
}
Error Message
: In instantiation of ‘void print(std::string, const X&
...) [with X = {ColumnValue, ColumnValue}; Y = {int,
std::__cxx11::basic_string,
std::allocator >}; std::string =
std::__cxx11::basic_string]’: :28:19: required from
here :12:5: error: unable to deduce
‘std::initializer_list&&’ from ‘{args#0, args#1}’ 12 |
for(auto val : {args...}) {
| ^~~ :12:5: note: deduced conflicting types for parameter ‘auto’ (‘ColumnValue’ and
‘ColumnValue >’)
The fact that ColumnValue is a template doesn't make any difference for the signature of print. We can just take a regular parameter pack and let the compiler figure out the different types.
Secondly we can't loop over a parameter pack. We can however use a fold-expression.
The end result would look something like this
template <typename... T>
void print(std::string firstArg, const T& ...args) {
(std::cout << ... << args.value) << std::endl;
}
If you want to insert a newline between each argument, you would need some kind of helper for that. The simplest idea would be.
template <typename T>
void print_helper(const T& arg) {
std::cout << arg << '\n';
}
template <typename... T>
void print(std::string firstArg, const T& ...args) {
(print_helper(args.value), ...);
}
The C++20 feature std::source_location is used to capture information about the context in which a function is called.
When I try to use it with a variadic template function, I encountered a problem: I can't see a place to put the source_location parameter.
The following doesn't work because variadic parameters have to be at the end:
// doesn't work
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
The following doesn't work either because the caller will be screwed up by the parameter inserted in between:
// doesn't work either, because ...
template <typename... Args>
void debug(const std::source_location& loc = std::source_location::current(),
Args&&... args);
// the caller will get confused
debug(42); // error: cannot convert 42 to std::source_location
I was informed in a comment that std::source_location works seamlessly with variadic templates, but I struggle to figure out how. How can I use std::source_location with variadic template functions?
The first form can be made to work, by adding a deduction guide:
template <typename... Ts>
struct debug
{
debug(Ts&&... ts, const std::source_location& loc = std::source_location::current());
};
template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;
Test:
int main()
{
debug(5, 'A', 3.14f, "foo");
}
DEMO
If your function has a fixed parameter before the variadiac arguments, like a printf format string, you could wrap that parameter in a struct that captures source_location in its constructor:
struct FormatWithLocation {
const char* value;
std::source_location loc;
FormatWithLocation(const char* s,
const std::source_location& l = std::source_location::current())
: value(s), loc(l) {}
};
template <typename... Args>
void debug(FormatWithLocation fmt, Args&&... args) {
printf("%s:%d] ", fmt.loc.file_name(), fmt.loc.line());
printf(fmt.value, args...);
}
int main() { debug("hello %s\n", "world"); }
Just put your arguments in a tuple, no macro needed.
#include <source_location>
#include <tuple>
template <typename... Args>
void debug(
std::tuple<Args...> args,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
And this works*.
Technically you could just write:
template <typename T>
void debug(
T arg,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
but then you'd probably have to jump through some hoops to get the argument types.
* In the linked-to example, I'm using <experimental/source_location> because that's what compilers accept right now. Also, I added some code for printing the argument tuple.
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
"works", but requires to specify template arguments as there are non deducible as there are not last:
debug<int>(42);
Demo
Possible (not perfect) alternatives include:
use overloads with hard coded limit (old possible way to "handle" variadic):
// 0 arguments
void debug(const std::source_location& loc = std::source_location::current());
// 1 argument
template <typename T0>
void debug(T0&& t0,
const std::source_location& loc = std::source_location::current());
// 2 arguments
template <typename T0, typename T1>
void debug(T0&& t0, T1&& t1,
const std::source_location& loc = std::source_location::current());
// ...
Demo
to put source_location at first position, without default:
template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
and
debug(std::source_location::current(), 42);
Demo
similarly to overloads, but just use tuple as group
template <typename Tuple>
void debug(Tuple&& t,
const std::source_location& loc = std::source_location::current());
or
template <typename ... Ts>
void debug(const std::tuple<Ts...>& t,
const std::source_location& loc = std::source_location::current());
with usage
debug(std::make_tuple(42));
Demo
Not a great solution but... what about place the variadic arguments in a std::tuple?
I mean... something as
template <typename... Args>
void debug (std::tuple<Args...> && t_args,
std::source_location const & loc = std::source_location::current());
Unfortunately, this way you have to explicitly call std::make_tuple calling it
debug(std::make_tuple(1, 2l, 3ll));
You can try make it:
#include <iostream>
#include <experimental/source_location>
struct log
{
log(std::experimental::source_location location = std::experimental::source_location::current()) : location { location } {}
template<typename... Args>
void operator() (Args... args)
{
std::cout << location.function_name() << std::endl;
std::cout << location.line() << std::endl;
}
std::experimental::source_location location;
};
int main()
{
log()("asdf");
log()(1);
}
DEMO
If you can accept the use of macros, you can write this to avoid explicitly passing in std::source_ location::current():
template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
#define debug(...) debug(std::source_location::current() __VA_OPT__(,) __VA_ARGS__)
The C++20 feature std::source_location is used to capture information about the context in which a function is called.
When I try to use it with a variadic template function, I encountered a problem: I can't see a place to put the source_location parameter.
The following doesn't work because variadic parameters have to be at the end:
// doesn't work
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
The following doesn't work either because the caller will be screwed up by the parameter inserted in between:
// doesn't work either, because ...
template <typename... Args>
void debug(const std::source_location& loc = std::source_location::current(),
Args&&... args);
// the caller will get confused
debug(42); // error: cannot convert 42 to std::source_location
I was informed in a comment that std::source_location works seamlessly with variadic templates, but I struggle to figure out how. How can I use std::source_location with variadic template functions?
The first form can be made to work, by adding a deduction guide:
template <typename... Ts>
struct debug
{
debug(Ts&&... ts, const std::source_location& loc = std::source_location::current());
};
template <typename... Ts>
debug(Ts&&...) -> debug<Ts...>;
Test:
int main()
{
debug(5, 'A', 3.14f, "foo");
}
DEMO
If your function has a fixed parameter before the variadiac arguments, like a printf format string, you could wrap that parameter in a struct that captures source_location in its constructor:
struct FormatWithLocation {
const char* value;
std::source_location loc;
FormatWithLocation(const char* s,
const std::source_location& l = std::source_location::current())
: value(s), loc(l) {}
};
template <typename... Args>
void debug(FormatWithLocation fmt, Args&&... args) {
printf("%s:%d] ", fmt.loc.file_name(), fmt.loc.line());
printf(fmt.value, args...);
}
int main() { debug("hello %s\n", "world"); }
Just put your arguments in a tuple, no macro needed.
#include <source_location>
#include <tuple>
template <typename... Args>
void debug(
std::tuple<Args...> args,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
And this works*.
Technically you could just write:
template <typename T>
void debug(
T arg,
const std::source_location& loc = std::source_location::current())
{
std::cout
<< "debug() called from source location "
<< loc.file_name() << ":" << loc.line() << '\n';
}
but then you'd probably have to jump through some hoops to get the argument types.
* In the linked-to example, I'm using <experimental/source_location> because that's what compilers accept right now. Also, I added some code for printing the argument tuple.
template <typename... Args>
void debug(Args&&... args,
const std::source_location& loc = std::source_location::current());
"works", but requires to specify template arguments as there are non deducible as there are not last:
debug<int>(42);
Demo
Possible (not perfect) alternatives include:
use overloads with hard coded limit (old possible way to "handle" variadic):
// 0 arguments
void debug(const std::source_location& loc = std::source_location::current());
// 1 argument
template <typename T0>
void debug(T0&& t0,
const std::source_location& loc = std::source_location::current());
// 2 arguments
template <typename T0, typename T1>
void debug(T0&& t0, T1&& t1,
const std::source_location& loc = std::source_location::current());
// ...
Demo
to put source_location at first position, without default:
template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
and
debug(std::source_location::current(), 42);
Demo
similarly to overloads, but just use tuple as group
template <typename Tuple>
void debug(Tuple&& t,
const std::source_location& loc = std::source_location::current());
or
template <typename ... Ts>
void debug(const std::tuple<Ts...>& t,
const std::source_location& loc = std::source_location::current());
with usage
debug(std::make_tuple(42));
Demo
Not a great solution but... what about place the variadic arguments in a std::tuple?
I mean... something as
template <typename... Args>
void debug (std::tuple<Args...> && t_args,
std::source_location const & loc = std::source_location::current());
Unfortunately, this way you have to explicitly call std::make_tuple calling it
debug(std::make_tuple(1, 2l, 3ll));
You can try make it:
#include <iostream>
#include <experimental/source_location>
struct log
{
log(std::experimental::source_location location = std::experimental::source_location::current()) : location { location } {}
template<typename... Args>
void operator() (Args... args)
{
std::cout << location.function_name() << std::endl;
std::cout << location.line() << std::endl;
}
std::experimental::source_location location;
};
int main()
{
log()("asdf");
log()(1);
}
DEMO
If you can accept the use of macros, you can write this to avoid explicitly passing in std::source_ location::current():
template <typename... Args>
void debug(const std::source_location& loc, Args&&... args);
#define debug(...) debug(std::source_location::current() __VA_OPT__(,) __VA_ARGS__)
Is it possible to statically "unroll" a parameter list at compile time, giving using one parameter in every "unroll" step? I think variadic templates are the way to go combined with partial template specialization, but I cannot get this example to run:
#include <iostream>
char static const text1[] = "Foo";
char static const text2[] = "FooBar";
template <char const * TEXT, unsigned int N, char const *... REST, unsigned int... Ns>
void doStuff() {
std :: cout << TEXT << "-" << N << std :: endl;
doStuff<REST..., Ns...>();
}
template <char const * TEXT, unsigned int N>
void doStuff() {
std :: cout << TEXT << std :: endl;
}
void doStuff() {}
int main() {
doStuff<text1,3,text2,5>();
return 0;
}
My expected output would be Foo-3\nFooBar-5
However, clang++ 3.8 gives me:
error: no matching function for call to 'doStuff'
doStuff<text1,3,text2,5>();
^~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:7:6: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'REST'
void doStuff() {
^
test.cpp:13:6: note: candidate template ignored: invalid explicitly-specified argument for template
parameter 'N'
void doStuff() {
^
In C++17, you might do something like
template <char const * TEXT, unsigned int N>
void doStuff() {
std::cout << TEXT << "-" << N << std::endl;
}
template <auto v1, auto v2, auto ... values>
void doStuff()
{
std :: cout << v1 << "-" << v2 << std :: endl;
doStuff<values...>();
}
Currently you have to pack your values by pair:
template<const char* S, int N>
struct pairValue {
static constexpr const char* s = S;
static constexpr int n = N;
};
template <typename ... Ts>
void doStuff()
{
const int dummy[] = {0, ((std::cout << Ts::s << "-" << Ts::n << std::endl), 0)...};
static_cast<void>(dummy); // Avoid warning for unused variable.
}
And call it:
doStuff<pairValue<text1, 3>, pairValue<text2, 5>>();
You can use them as parameters to work around it:
#include <iostream>
#include<type_traits>
char static const text1[] = "Foo";
char static const text2[] = "FooBar";
constexpr void doStuff() {}
template <typename T, typename U, typename... O>
constexpr
std::enable_if_t<
std::is_same<T, const char *>::value
and std::is_same<U, int>::value
> doStuff(T str, U num, O... o) {
std :: cout << str << "-" << num << std :: endl;
doStuff(o...);
}
int main() {
doStuff(text1,3,text2,5);
return 0;
}
// some arbitrary function
template<typename T>
void log( T&& obj )
{
std::cout << obj << std::endl;
}
// arbitrary transformation
template<typename T>
T convert(T&& obj) {
return obj;
}
template<template <typename> typename F, typename... T>
void callOn( F<T> func, /// issue: what's the type of func?
T&&... params)
{
func(std::forward<T>(convert(std::forward<T>(params)))...);
}
int main()
{
callOn(log, -1, -2.0);
return 0;
}
Is this possible at all?
Compiler complains: no matching function for call to 'callOn(, ..>). Why ?
Update: suppose log is not unary function
template<typename T>
void log(T&& value) { std::cout << value << std::endl; }
template<typename First, typename... Rest>
void log(First&& f, Rest&&... rest)
{
std::cout << f << ",";
log(std::forward<Rest>(rest)...);
}
callOn takes type "template " which doesn't match the type of log? How to specify the type of func?
Use a function object. Here's a compilable example:
#include <utility>
#include <iostream>
struct Log
{
template<typename T> void operator()(T&& t) {
std::cout << t << std::endl;
}
template<typename T, typename... Rest> void operator()(T&& t, Rest&&... rest)
{
std::cout << t << ", ";
(*this)(std::forward<Rest>(rest)...);
}
};
template<typename T>
T convert(T&& obj) {
return obj;
}
template<typename F, typename... T>
void callOn(F funcobj, T&&... params)
{
funcobj(std::forward<T>(convert(std::forward<T>(params)))...);
}
int main()
{
callOn(Log(), -1, -2.17, "abc");
return 0;
}
I don't think the syntax of template <typename> typename F is correct, and Standard specified in 14.3.3/1 that "A template-argument for a template template-parameter shall be the name of a class template or an alias template", not a function template. If you want to pass a function template, you can declare the parameter as a pointer to function:
template<typename... T>
void callOn( void (*func)(T&&...params),
T&&... params)
{
//same as before
}
And when callOn(log, -1, -2.0); is called, the type of T is not deduced from log but from {-1, -2.0} to be {int, double}, then func is initialized from the pointer pointed to log<int, double>(int&&, double&&).